Skip to content

29. MySQL磁盘 IO 问题

前面提到的 SQL 优化、数据库对象优化、数据库参数优化,以及应用程序优化等, 大部分都是想通过减少或延缓磁盘读写来减轻磁盘 I/O 的压力及其对性能的影响。

1. 使用磁盘阵列

RAID(Redundant Array of Inexpensive Disks):廉价磁盘冗余阵列,通常就叫做磁盘阵列。

RAID 级别 特性 优点 缺点
RAID 0 也叫条带化(Stripe),按一定 的条带大小(Chunk Size)将数 据依次分布到各个磁盘,没有 数据冗余 数据并发读写速度快,无额 外磁盘空间开销,投资省 数据无冗余保护,可靠性 差
RAID 1 也叫磁盘镜像(Mirror),两个 磁盘一组,所有数据都同时写 入两个磁盘,但读时从任一磁 盘读都可以 数据有完全冗余保护,只要 不出现两块镜像磁盘同时损 坏,不会影响使用;可以提 高并发读性能 容量一定的话,需要 2 陪 的磁盘,投资比较大
RAID 10 是 RAID 1 和 RAID 0 的结合,也 叫 RAID 1+0。先对磁盘做镜像, 再条带话,使其兼具 RAID 1 的可靠性和RAID 0的优良并发读写性能 可靠性高,并发读写性能优 良 容量一定的话,需要 2 陪 的磁盘,投资比较大
RAID 4 象 RAID 0 一样对磁盘组条带 化,不同的是:需要额外增加 一个磁盘,用来写各 Stripe 的 校验纠错数据 RAID 中的一个磁盘损坏,其 数据可以通过校验纠错数据 计算出来,具有一定容错保 护能力;读数据速度快 每个 Stripe 上数据的修改 都要写校验纠错块,写性 能受影响;所有纠错数据 都在同一磁盘上,风险大, 也会形成一个性能瓶颈; 在出现坏盘时,读性能会下降
RAID 5 是对 RAID 4 的改进:将每一个 条带(Stripe)的校验纠错数据 块也分布写到各个磁盘,而不 是写到一个特定的磁盘 基本同 RAID 4,只是其写性 能和数据保护能力要更强一 点 写性能不及 RAID 0、RAID 1 和 RAID 10,容错能力也不 及 RAID 1;在出现坏盘时, 读性能会下降

根据数据读写的特点、可靠性要求,以及投资预算等来选择合适的 RAID 级别,比如:

  • 数据读写都很频繁,可靠性要求也很高,最好选择 RAID 10;
  • 数据读很频繁,写相对较少,对可靠性有一定要求,可以选择 RAID 5;
  • 数据读写都很频繁,但可靠性要求不高,可以选择 RAID 0。

1.1 虚拟文件卷或软 RAID

一些操作系统中提供的软件包,也模拟实现了一些 RAID 的特性,但性能上不如硬 RAID 。

比如,Linux 下的逻辑卷(Logical Volume)系统 lvm2,支持条带化(Stripe);Linux 下的 MD(Multiple Device)驱动,支持 RAID 0、RAID 1、RAID 4、RAID 5、RAID 6 等。

默认情况下,创建的数据库和表都存放在参数 datadir定义的目录下。在这种情况下,我们就可以利用操作系统的符号连接(Symbolic Links)将不同的数据库或表、索引指向不同的物理磁盘,从而达到分布磁盘 I/O 的目的。

  1. 将一个数据库指向其他物理磁盘。

其方法是先在目标磁盘上创建目录,然后再创建从 MySQL 数据目录到目标目录的符号连接:

shell> mkdir /otherdisk/databases/test
shell> ln -s /otherdisk/databases/test /path/to/datadir
2. 将MyISAM(其他存储引擎的表不支持)表的数据文件或索引文件指向其他物理磁盘。 
    1. 对于新建的表,可以通过在CREATE TABLE语句中增加`DATA DIRECTORY`和`INDEX DIRECTORY`选项来完成(对于分区表无效),例如:
Create table test(id int primary key,
Name varchar(20))
Type = myisam
DATA DIRECTORY = '/disk2/data'
INDEX DIRECTORY = '/disk3/index'

​ 2. 对于已有的表,可以先将其数据文件(.MYD)或索引文件(.MYI)转移到目标磁盘,然后再建立符号连接即可。需要说明的是表定义文件(.frm)必须位于MySQL数据文件目录下,不能用符号连接。

注意:使用Symbolic Links存在一定的安全风险,如果不使用Symbolic Links,应通过启动参数skip-symbolic-links禁用这一功能。

3. 禁止操作系统更新文件的 atime 属性

atime是 Linux/UNIX 系统下的一个文件属性,每当读取文件时,操作系统都会将读操作发生的时间回写到磁盘上。可以通过设置文件系统的 mount 属性,阻止操作系统写 atime 信息,以减轻磁盘 I/O 的负担。在 Linux 下的具体做法是:

  1. 修改文件系统配置文件/etc/fstab,指定 noatime 选项:

    LABEL=/home /home ext3 noatime 1 2
    
  2. 然后重新 mount 文件系统:

    mount -oremount /home
    

完成上述操作,以后读 /home 下文件就不会再写磁盘了 。

4. 用裸设备(Raw Device)存放 InnoDB 的共享表空间

MyISAM 存储引擎有自己的索引缓存机制,但数据文件的读写完全依赖于操作系统,操作系统磁盘 I/O 缓存对 MyISAM 表的存取很重要。

但 InnoDB 存储引擎与 MyISAM 不同它采用类似 Oracle 的数据缓存机制来 Cache 索引和数据,操作系统的磁盘 I/O 缓存对其性能不仅没有帮助,甚至还有反作用。

因此,在 InnoDB 缓存充足的情况下,可以考虑使用 Raw device 来存放 InnoDB 共享表空间:

  1. 修改MySQL配置文件,在innodb_data_file_path参数中增加裸设备文件名并指定newraw属性:
[mysqld]
innodb_data_home_dir=
innodb_data_file_path=/dev/hdd1:3Gnewraw;/dev/hdd2:2Gnewraw
  1. 启动MySQL,使其完成分区初始化工作,然后关闭MySQL。此时还不能创建或修改InnoDB表。
  2. innodb_data_file_path中的newraw改成raw :
......
class=programlisting[mysqld]
innodb_data_home_dir=
innodb_data_file_path=/dev/hdd1:3Graw;/dev/hdd2:2Graw
......
  1. 重新启动即可开始使用。