目录
io线程数innodb_read_io_threads、innodb_write_io_threads
innodb IOPS控制innodb_io_capacity
innodb IO调度重要参数innodb_flush_method
注意区分缓存IO、直接IO、延时写,同步IO和异步IO的区别
相关参数
io线程数innodb_read_io_threads、innodb_write_io_threads
静态参数,默认4。取值范围[1-64]
innodb_read_io_threads 读io线程数
innodb_write_io_threads写io线程数
innodb IOPS控制innodb_io_capacity
动态参数,innodb后台任务可用的iops值,比如flush buffer pool和merge change buffer
innodb_io_capacity_max
动态参数,当innodb_io_capacity的iops对写盘操作延时,innodb的iops限制可以超过innodb_io_capacity,在这种情况下iops的最大值为innodb_io_capacity_max
异步IO控制innodb_use_native_aio
innodb可以使用native aio,控制启用该功能的参数就是innodb_use_native_aio。
innodb_use_native_aio默认打开,静态参数。
innodb只能在linux中使用aio,在其他类unix系统中,innodb会使用同步IO,windows只能使用AIO。
innodb IO调度重要参数innodb_flush_method
innodb_flush_method:控制innodb写磁盘的方式。innodb会调用OS内核写盘函数,直接影响数据库写盘方式。
innodb_flush_method默认是null,null在类unix系统中默认为fsync
可配置项包含如下:
fsync:fsync去写数据和日志。这种模式下数据会先落到操作系统内核缓冲区
,然后再从操作系统内核缓冲区落到磁盘。fsync()会确保将缓冲数据和fd相关的所有元数据都刷新到磁盘上
O_DSYNC:用o_sync写log,fsync写数据。innodb不会直接使用O_DSYNC(仅刷数据不刷元数据),因为在很多系统中存在问题。o_sync同样也要刷脏页到磁盘中,跟fsync有些类似。o_sync会返回写盘完成的验证以保证数据安全写入,但是真实效果如何还需要进一步验证。
littlesync:内部测试使用,不推荐
nosync:内部测试使用,不推荐
O_DIRECT:用O_DIRECT去open打开文件,这种open模式不会经过os cache。但是mysql的o_direct仍然会用到fsync,mysql会用fsync去同步os cache的元数据到fs,元数据量相对较少。O_DIRECT仍然需要调用到sync
O_DIRECT_NO_FSYNC:调用O_DIRECT去flush IO,在写操作时不会用fsync去同步元数据变更。在xfs和ext4文件系统中不推荐使用,在某些系统中存在丢失数据的可能。
常用配置是fsync、o_dsync和O_DIRECT。弄明白三者的区别需要先了解IO调度机制一节
监控
fsync总量
show global status like '%fsync%';
+------------------------------+-------+
| Variable_name | Value |
+------------------------------+-------+
| Innodb_data_fsyncs | 658 |
| Innodb_data_pending_fsyncs | 0 |
| Innodb_os_log_fsyncs | 402 |
| Innodb_os_log_pending_fsyncs | 0 |
+------------------------------+-------+
aio/io线程监控
show engine innodb status\G
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (log thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
I/O thread 3 state: waiting for completed aio requests (read thread)
I/O thread 4 state: waiting for completed aio requests (read thread)
I/O thread 5 state: waiting for completed aio requests (read thread)
I/O thread 6 state: waiting for completed aio requests (write thread)
I/O thread 7 state: waiting for completed aio requests (write thread)
I/O thread 8 state: waiting for completed aio requests (write thread)
I/O thread 9 state: waiting for completed aio requests (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
383 OS file reads, 1070 OS file writes, 666 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
IO调度机制
页高速缓存
磁盘高速缓存是一种软件机制,它允许系统把通常存放在磁盘上的一些数据保留在RAM中,以便访问数据时减少磁盘访问(缓存命中)。页高速缓存是一种完整页操作的磁盘高速缓存。
linux内核的页缓存同样有脏页的说法。在写数据时多次写操作可以合并对相应磁盘块进行一次物理更新。这就是页缓存的延时写特性。延时写虽然提高了磁盘写入速度,但也是有危害的。一个脏页可能直到系统关闭时都逗留在页缓存中,如果发送异常崩溃ram中的脏页缓存可能会丢失。
进程可以通过调用sync,fsync,fdatasync等方法来执行脏页的刷新。这样的调用就是同步写。
sync:把所有的脏页刷新到磁盘
fsync:把特定的打开文件的脏块刷新到磁盘
fdatasync:与fsync类似,但不刷新文件的索引节点块
缓存IO
缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中。
缓存IO的优势
1.加快IO查返回速度,查询IO可能会命中存储,应用程序查询返回响应更快
2.缓存IO默认是延时写的,可以简化IO写的复杂度,比如对同一个块的对此写入可以合并为一次写入磁盘的操作,减少了磁盘IO写的压力
缓存IO的劣势
1.浪费ram空间,磁盘高速缓存会占用一部分内存
2.在应用程序和磁盘之间多出一块存储数据的内存区域,这样多出来的复制操作仍然是消耗cpu的。
直接 IO(O_DIRECT)
凡是通过直接 I/O 方式进行数据传输,数据均直接在用户地址空间的缓冲区和磁盘之间直接进行传输,完全不需要页缓存的支持。O_DIRECT是OPEN()的方式,在写IO发生时IO调度会读取O_DIRECT为1的标记,表示使用直接IO。
操作系统层提供的缓存往往会使应用程序在读写数据的时候获得更好的性能,但是对于某些特殊的应用程序,比如说数据库管理系统这类应用,他们更倾向于选择他们自己的缓存机制,因为数据库管理系统往往比操作系统更了解数据库中存放的数据,数据库管理系统可以提供一种更加有效的缓存机制来提高数据库中数据的存取性能。
mysql的O_DIRECT直接对磁盘进行写入,它需要额外的操作来同步磁盘元数据信息到操作系统上,这个操作就是sync。mysql的method为O_DIRECT时,使用直接IO刷脏页和日志,sync刷元数据到文件系统
异步IO和同步IO
同步IO和异步IO是应用侧线程调度IO的规则,跟怎么写入磁盘没有太大关系。
同步IO:
异步IO在接受到IO请求后就返回给应用程序,写入磁盘操作完成后再次返回完成操作。在完成之前应用程序可以做其他任务。同步IO在接收到IO请求后不会返回任何信息,当写入完成后才会返回写入完成。
异步IO:
Linux 异步 I/O 是 Linux 2.6 中的一个标准特性,其本质思想就是进程发出数据传输请求之后,进程不会被阻塞,也不用等待任何操作完成,进程可以在数据传输的时候继续执行其他的操作。相对于同步访问文件的方式来说,异步访问文件的方式可以提高应用程序的效率,并且提高系统资源利用率。直接 I/O 经常会和异步访问文件的方式结合在一起使用。
AIO:(async IO)
AIO包括Kernel Native AIO(内核支持,需要libaio包,常用)\Glibc AIO等
使用kernel native AIO的限制:
1.raw设备和用O_DIRECT打开的块设备(raw现在在linux中很少见)
2.O_DIRECT打开的文件系统上的文件,如果不是用O_DIRECT打开的文件,不会报错,但是也不会使用AIO,而是同步IO(这也是为什么直接IO和AIO会一起使用的原因)
3.不支持AIO fsync(也就是说fsync+aio是没效果的)
kernel AIO:
http://lse.sourceforge.net/io/aio.html?spm=a2c6h.12873639.0.0.28ac1816iPZQzx
https://developer.aliyun.com/article/341711
aio-max-nr
通过/proc/sys/fs/aio-max-nr查看最大逻辑块号。如果使用AIO,aio-max-nr不能设置的过小,aio-nr一但达到max上限就会报错,特别是在多server的os中。
AIO的优势:
在读写pending较多的系统中使用AIO是有好处的。当IO并发提升后,AIO的IO调度模式可以更好的处理并发
AIO的劣势:
使用AIO时无法控制IO线程的处理IO请求的并行度。比如在IO写较多的场景中,IO线程并行处理了太多的写请求,磁盘的写IO非常高,这会导致IO读的速度下降。
innodb的同步IO
sql可能会在IO请求中排队,一个innodb io线程一次只能处理请求队列中的一个请求。当一个IO线程的IO请求完成并且IO返回完成消息时,这个IO线程才可以进行处理下一个IO队列中的请求。innodb IO线程数为innodb_read_io_threads和innodb_write_io_threads,均默认值为4,也就是说同步IO下最多分别有4个线程处理读和写请求(读写一起是8个线程)。
innodb的异步IO
AIO下IO线程会直接将IO请求下发到磁盘(前面已经提到,使用AIO通常必须使用O_DIRECT打开文件),一个IO线程下发IO请求并收到磁盘返回”接收成功“的信息,该线程可以不需要等待IO返回”完成“就可以处理下一个IO队列的请求。所以这种模式下IO并行度不被innodb_read_io_threads和innodb_write_io_threads强限制。
问题深入
注意区分缓存IO、直接IO、延时写,同步IO和异步IO的区别
缓存IO:IO调度需要经过操作系统内核的页高速缓存,读和写均经过了该缓冲区。读可以发生缓存命中,写带来合并刷脏页的特性。其中sync、fsync、fdatasync均是刷脏页的方式,使用这些方式均需要通过缓存
直接IO:IO调度不经过页高速缓存,直接将应用程序内存地址中的数据写入到磁盘中,O_DIRECT就是直接IO
延时写:延时写是缓存IO的特性。缓存IO在写数据时可以不需要将数据写入磁盘而是暂时保存在页缓存中,这样可以减少磁盘写的压力。当应用程序在写数据时调度sync、fsync、fdatasync中的任何一个都可以保证脏页被刷新到磁盘,并不是延时写
同步IO:同步IO异步IO说的是进程或线程调用IO的模式,它不是底层IO方法,而是进程/线程的一种规则。比如mysql io线程有一个写入请求,io调度必须完成这个请求才可以返回响应给线程,写盘期间这个线程只能等待不可用做其他事情
异步IO:仍然有一个mysql io线程发出写入请求,磁盘在接收到请求后即可返回响应(不是完成),写盘操作完成后再次返回响应(完成写入)。在写盘期间这个线程可以做其他事情,对于mysql io thread来说就是继续下发io请求到磁盘。
为什么要使用O_DIRECT?
”fsync将数据写入缓存,应该将innodb_flush_method设置为O_DIRECT保证写入磁盘“这句话是不对的。当innodb_flush_method为fsync或者O_DIRECT时,都可以保证数据被写入到磁盘,只是他们写盘数据来源不同罢了(实际上write()才是延时写,一般系统调用read(),write()来存取,而mysql innodb_flush_method没有这个选项)。而o_dsync调用了o_sync,仍然会过页缓存,性能上跟fsync不会差太多。仍然建议使用O_DIRECT,因为在数据库内存和磁盘间仍然多了一个内存:页缓存,它是linux内核开辟出来提升IO效率的,不针对特别的应用场景。将数据库内存的脏数据拷贝到页缓存,再将页缓存的脏数据拷贝到磁盘,这明显就多次一举。由于数据库更加了解数据模型和写入规则,我们应当使用数据库的内存(buffer pool)和数据库刷脏页机制。
“if you use a Unix-like operating system and your RAID controller has a battery-backed writecache, we recommend that you use O_DIRECT. If not, either the default or O_DIRECT will probably be the best choice, depending on your application”
实际上除了OS cache外,raid也有cache,raid卡都是有备用电池的。o_direct写磁盘整列时也没有直接写入磁盘,而是写到了raid缓存。对于上层应用来说,可以算写入磁盘了。
当刷盘变慢时应该做些什么?
1.buffer pool。如果内存命中率明显减低,IO压力提升,可能会带来刷盘变慢。应该增加innodb_buffer_pool_size的大小,降低物理IO次数
2.调整flush method为o_direct。因为o_direct不会经过页缓存,一般来说速度会比o_dsync和fsync快一些。最好对当前磁盘环境做全面测试。
3.可以考虑使用aio。linux中默认打开
4.用deadline I/O scheduler或者直接上SSD。IO scheduler相当于机械硬盘分配IO的方式,默认是CFQ平均分配。SSD由于结构不同,没有I/O scheduler的说法。
5.调整innodb_read_io_threads、innodb_write_io_threads,增加io线程数
6.调整innodb_io_capacity和innodb_io_capacity_max,增加iops上限
7.应用侧。是否有多个写入操作同时进行,是否可用错开高峰等等
文档参考
mysql官方文档
《深入理解linux内核》
o_direct和o_sync:https://dba.stackexchange.com/questions/23943/innodb-flush-method-o-direct-vs-o-dsync-performance-impact-on-ext3-with-lvm-disk
AIO:http://lse.sourceforge.net/io/aio.html?spm=a2c6h.12873639.0.0.28ac1816iPZQzx
AIO:https://developer.aliyun.com/article/341711
direct_no_fsync:https://bugs.mysql.com/bug.php?id=94912%20%20%20%20%20Bug%20#94912%20O_DIRECT_NO_FSYNC%20possible%20write%20hole
direct io&buffer io:https://www.ibm.com/developerworks/cn/linux/l-cn-directio/index.html