等待的线程
io_handler_thread 等线程的主函数,每个线程都对应一个对列,read,write是4个线程对应一个队列
fil_aio_wait 主要是等待IO结束
buf_page_io_complete 读页面的内容,释放对页面的锁
读页面的过程
buf_read_page_low Sets the io_fix flag and sets an exclusive lock on the buffer frame. The flag is cleared and the x-lock released by an i/o-handler thread.
fil_io
os_aio
os_aio_array_reserve_slot 上队列里面找到空闲的,调用io_prep_pread 或 io_prep_pwrite
os_aio_linux_dispatch 调用 io_submit, 一个页面就调用,而不是多个页面一块调
read ahead
http://dev.mysql.com/doc/refman/5.5/en/innodb-performance-read_ahead.html
看来mysql已放弃,读一个页面时,把这个页面相邻的页面也读进来的random read ahead,
要执行liner read ahead,需要有三个前提条件
1。当前正在读的是border页面,并且是第一次读入内存,当等待这个要读的页面读完后
2。在当前这个页面区间内,已有阀值个页面被读入,默认的阀值是56,最少有56个相邻页面,才进行read ahead下一个区间,是循环64次,每次读入一个页面
3。本页面所在的区间,和下次要读的区间是物理相邻的,并且不等待返回这个区间的返回
比如:
0--63 64--127 这个区间中,当读到63页面时,把下一个区间64个页面读入
win下面缓存读写和直接读写的不同点,对于直接读写对应的参数值为async_unbuffered,对于缓存读写对应的参数值为normal。主要是分析open和write的不同。
1.Win直接读写。
mysql帮助文档里面指明在win下面只能使用async_unbuffered并且不能更改,这个参数是使用不带系统缓存的异步读写方式。数据文件和日志都走的是Windows native AIO这条路,只不过日志在wirte之后,会等待异步写返回。
Open:
CreateFile((LPCTSTR) name,
GENERIC_READ | GENERIC_WRITE,
share_mode,
NULL,
create_flag,
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
NULL);
Write: 对于数据文件和日志文件是一样的
WriteFile(file, buf, (DWORD)n, &len, &(slot->control));
在日志的异步写完之后,会有一个WaitForSingleObject操作,等待日志写操作返回。
2.Win缓存读写
Win下面通过查看源码,发现还支持normal这个值,当设置这个值时,实现的就是缓存读写,没有用到任何Simulated AIO和Windows native AIO的数据结构和线程,数据文件和日志文件的读写操作就是直接调用系统函数。
Open: 对于数据文件和日志文件是一样的
CreateFile((LPCTSTR) name,
GENERIC_READ | GENERIC_WRITE,
share_mode,
NULL,
create_flag,
0,
NULL);
Write:对于数据文件和日志文件是一样的
WriteFile(file, buf, (DWORD) n, &len, NULL);
以下分析Linux下面缓存读写和直接读写的不同点,对于直接读写对应的参数值为O_DIRECT,对于缓存读写对应的参数值为O_DSYNC、fdatasync。
1.Linux下面直接读写
这个参数是使用不带系统缓存的异步读写方式。数据文件走的是Linux native AIO这条路,日志文件走的是缓存读写这条路。
Open:
对于数据文件:open(name, O_RDWR | O_DIRECT, os_innodb_umask);
对于日志文件:open(name, O_RDWR, os_innodb_umask);
Write:
对于数据文件:走的是Linux native AIO这条路
对于日志文件:pwrite(file, buf, (ssize_t)n, offs);
2.Linux下面缓存读写O_DSYNC
数据文件和日志文件走的是缓存读写这条路。日志文件中打开时多了一个参数。
Open:
对于数据文件:open(name, O_RDWR , os_innodb_umask);
对于日志文件:open(name, O_RDWR | O_SYNC, os_innodb_umask);
Write: 对于数据文件和日志文件是一样的
pwrite(file, buf, (ssize_t)n, offs);
3.Linux下面缓存读写fdatasync
数据文件和日志文件走的是缓存读写这条路。
Open: 对于数据文件和日志文件是一样的
open(name, O_RDWR , os_innodb_umask);
Write: 对于数据文件和日志文件是一样的
pwrite(file, buf, (ssize_t)n, offs);
从上面可以看过,对于直接读写,在win和Linux,对于日志的实现是不一样的,win下面是直接异步读写,加一个等待操作;Linux下面是缓存读写。
在Win下面,直接读写走的是Windows Native AIO这条路,缓存读写就是没用使用Simulated AIO和Windows Native AIO的线程和数据结构,直接调用读写函数。
在Linux下面,直接读写走的是Linux Native AIO这条路,缓存读写走的是缓存读写这条路。
1.1.1.1 mysql中的flush
write操作后,我们还调用了fdatasync来确保文件数据flush到了disk上。fdatasync返回成功后,那么可以认为数据已经写到了磁盘上。像这样的flush的函数还有fsync、sync。Sync函数表示将文件在OS cache中的数据排入写队列,并不确认是否真的写磁盘了,所以sync并不可以靠。fsync函数只对由文件描述符filedes指定的单一文件起作用,并且等待写磁盘操作结束,然后返回。fsync可用于数据库这样的应用程序,这种应用程序需要确保将修改过的块立即写到磁盘上。fdatasync函数类似于fsync,但它只影响文件的数据部分。而除数据外,fsync还会同步更新文件的属性。这里需要特别说明一下的是目前glibc中fdatasync函数的实现已经和fsync一摸一样。
忽略文件打开的过程,通常我们会说“写文件”有两个阶段,一个是调用write我们称为写数据阶段(其实是受open的参数影响),调用fsync(或者fdatasync)我们称为flush阶段。对于日志文件的flush主要由下面的系统参数决定。
参数Innodb_flush_method(Linux)可以设定为:Fdatasync、O_DSYNC、O_DIRECT。我们看看这个三个参数是如何影响程序MySQL对日志和数据文件的操作:
| Open log | Flush log | Open datafile | Flush data |
Fdatasync | O_RDWR | fsync() | O_RDWR | fsync() |
O_DSYNC | O_RDWR|O_SYNC |
| O_RDWR | fsync() |
O_DIRECT |
| fsync() | O_DIRECT | fsync() |
参数Innodb_flush_method(Win)可以设定为:normal、async_unbuffered我们看看这个二个参数是如何影响程序MySQL对日志和数据文件的操作:
| Open log | Flush log | Open datafile | Flush data |
normal |
| FlushFileBuffers() |
| FlushFileBuffers() |
async_unbuffered | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING | FlushFileBuffers() | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING | FlushFileBuffers() |
对于mysql中的日志,flush的时机还由参数innodb_flush_log_at_trx_commit决定
1(默认) | 在事务commit时,把日志write进硬盘,之后flush |
0 | 隔1秒种,把日志write进硬盘,之后flush;事务commit时,无操作 |
2 | 在事务commit时,把日志write进硬盘,不flush;每隔1秒,进行flush |
默认1是能保证数据库的ACID属性的。当为其它值时,能提高数据库的性能。
除上文档上面介绍的,对于日志,通过查看源代码,发现在Simulated AIO或是Native AIO这种写操作结束之后,在write waiter这种线程中,每一次写操作完成时,也会调用flush。具体看下面代码。
if (srv_unix_file_flush_method !=SRV_UNIX_O_DSYNC
&& srv_unix_file_flush_method !=SRV_UNIX_NOSYNC
&& srv_flush_log_at_trx_commit != 2) {
fil_flush(group->space_id);
}
在Linux下面,同步IO和异步IO与文件是否带缓存没有直接关系, 对于打开的文件,支持缓存读写和直接读写。
对于日志文件,在事务start和commit时,是用带缓存的同步IO写入的,在check point时,用的是带缓存的异步IO写入
对于数据文件,在double write buffer时,是采用带缓存的同步IO写入,在普通的表写入时,是采用不带缓存的异步IO写入
在win下面,由于不支持同一个文件,既带缓存读写,又不带缓存读写,按默认的配置,是根据操作系统的版本,采用对数据文件和日志文件都是异步不带缓存的读写,日志文件在写完后,有一个wait的操作,等待写操作返回。
对于flush,
在linux下面,日志文件有带缓存的同步写,所以在事务commit和每次写操作完成后都有flush.对于数据文件,是在master thread里面每隔10秒,在把脏页面向硬盘写时完成的,此时先有double buffer write
对于Win下面,采用异步不带缓存的写,但是也有flush,