innodb_flush_method 与 File I/O

之前在研究MySQL的一个参数innodb_flush_method时,就涉及到了fsync/fdatasync这些库函数。接下来就简单的分析一下sync/fsync/fdatasync的区别。

        sync():int sync( void )这就是它的原型,A call to this function will not return as long as there is data which has not been written to the device,sync()同步写,没有写到物理设备就不会返回,但是现实中并不是这样的。在kernel的手册上有解释:BUGS部分(linux中用man查看命令的时候不是都有一个BUGS部分么,就是指的那个)According to the standard specification (e.g., POSIX.1-2001), sync() schedules the writes, but may return before the actual writing is done.  However, since version 1.3.20 Linux does actually wait.  (This still does not guarantee data integrity: modern disks have large caches.)也就是sync()负责将这些写物理设备的请求放入写队列,但是不一定写真正被完成了。

        fsync(int fd):The fsync function can be used to make sure all data associated with the open file fildes is written to the device associated with the descriptor。fsync()负责将一个文件描述符(什么是文件描述符,它是unix、类unix系统打开文件的一种方式,应该相当于打开文件的一个句柄一样)打开的文件写到物理设备,而且是真正的同步写,没有写完成就不会返回,而且写的时候讲文件本身的一些元数据都会更新到物理设备上去,比如atime,mtime等等。

        fdatasync(int fd):When a call to the fdatasync function returns, it is ensured that all of the file data is written to the device。它只保证开打文件的数据全部被写到物理设备上,但是一些元数据并不是一定的,这也是它与fsync的区别。

        这三个库函数都简单的介绍完,那么为什么需要它们三个呢?最简单的说是从应用的需求来考虑的,sync是全局的,对整个系统都flush,fsync值针对单个文件,fdatasync当初设计是考虑到有特殊的时候一些基本的元数据比如atime,mtime这些不会对以后读取造成不一致性,因此少了这些元数据的同步可能会在性能上有提升(但fsync和fdatasync两者的性能差别有多大?这个不知道有谁测过没)。所以说三者是根据不同的需求而定的。

        接下来谈谈flush dirty page,也就是前面说的同步写(没写完的话阻塞后面,直到写完才返回)。为什么是刷脏页?脏页表示缓存中的页(一般也就是内存中)也物理设备上的页处于不一致,不一致是由于在内存中被修改了。所以为了持久化这种内存中的修改,需要flush到物理设备上。根据我的理解,一般来说缓存分成这几种:1>应用程序自己带了缓存,比如InnoDB的buffer pool;2>os层面上的缓存 ;3>磁盘设备自己的缓存,比如raid卡一般都管理着自己的缓存;4>磁盘本身或许会有一点点缓存(这个不确定,自己猜想的,这个即使有估计也是极小的)。好了,那么大部分的时候我们说的flush dirty page都是指从应用程序的缓存->os的缓存->物理设备,如果物理设备没有缓存的话,此时也就相当于持久化成功,但是像磁盘做了raid,raid卡有缓存的话,实际上还没真正持久化成功,因为此时还只到了raid卡的缓存,没到物理设备,但是由于raid卡一般都带有备用电池,所以即使此时断电也不会造成数据丢失。

       刚才说了很多时候应用自己也有缓存机制,那么你是否想过此时与os的缓存有重复呢?答案是:会的。刚才说了我是通过研究MySQL的一个参数innodb_flush_method注意这些的,innodb_flush_method表示flush策略,MySQL提供了fdatasync/O_DSYNC/O_DIRECT这三个选项,默认是fdatasync,我这里主要说明为什么会提供选项:O_DIRECT。这个选项告诉os,InnoDB在读写数据的时候都不经过os的缓存,因为刚才说过InnoDB会维护自己的缓存buffer pool,如果还使用os的缓存那么两者就会有一定的重复。有数据表明,如果是大量随机写入操作,O_DIRECT会提升效率。但是顺序写入和读取效率都会降低。所以使用O_DIRECT需要谨慎。

陶方在《innodb_flush_method带来的性能影响》中从实验角度比较了fdatasync,O_DSYNC和O_DIRECT在性能上的差异。本文将试图从Linux/Unix"文件I/O"(unbuffered I/O)的角度来解释innodb_flush_method是如何影响MySQL的I/O。

innodb_flush_log_at_trx_commit参数确定日志文件何时write、flush。
innodb_flush_method则确定日志及数据文件如何write、flush。

在Linux下,innodb_flush_method可以取如下值:fdatasync, O_DSYNC, O_DIRECT,那这三个值分别是如何影响文件写入的?首先我们需要先来了解Linux的文件I/O是如何工作的。

先来看看Linux/Unix文件I/O的一个典型例子:(Linux 2.6.24测试,gcc编译)

/**
	A test about syscall of File I/O
	Author:supu@TaobaoDBA
	[email protected] http://orczhou.com http://www.taobaodba.com
*/

#include   "stdlib.h"           /* for exit */
#include   "unistd.h"           /* for write fdatasync*/
#include    "fcntl.h"           /* for open  */
int main(void){
	int fd;
	if((fd=open("/home/zzx/test.file",O_WRONLY|O_APPEND|O_DSYNC))<0){
		exit(1);
        }
        char buff[]="abcdef";
        if(write(fd,buff,6)!= 6){
                exit(2);
        }
        if(fdatasync(fd)==-1){
                exit(3);
        }
        exit(0);
}


程序描述了一般的文件I/O操作的三个过程open、write、fdatasync,分别是打开文件、写文件、flush操作(将文件缓存刷到磁盘上)。

一、Open阶段

open("test.file",O_WRONLY|O_APPDENT|O_SYNC))

系统调用Open会为该进程一个文件描述符fd【附录2】。这里使用了O_WRONLY|O_APPDENT|O_SYNC打开文件:

  1. O_WRONLY表示我们以"写"的方式打开,告诉内核我们需要向文件中写入数据;
  2. O_APPDENT告诉内核以"追加"的方式写文件;
  3. O_DSYNC告诉内核,当向文件写入数据的时候,只有当数据写到了磁盘时,写入操作才算完成(write才返回成功)。和O_DSYNC同类的文件标志,还有O_SYNC,O_RSYNC,O_DIRECT。
    • O_SYNC比O_DSYNC更严格,不仅要求数据已经写到了磁盘,而且对应的数据文件的属性(例如文件长度等)也需要更新完成才算write操作成功。可见O_SYNC较之O_DSYNC要多做一些操作。
    • O_RSYNC表示文件读取时,该文件的OS cache必须已经全部flush到磁盘了【附录3】
    • 如果使用O_DIRECT打开文件,则读/写操作都会跳过OS cache,直接在device(disk)上读/写。因为没有了OS cache,所以会O_DIRECT降低文件的顺序读写的效率。

二、Write阶段

write(fd,buf,6)

在使用open打开文件获得文件描述符之后,我们就可以调用write函数来写入数据了,write会根据前面的open参数不同,而表现不同。

三、Flush阶段

fdatasync(fd) == -1

write操作后,我们还调用了fdatasync来确保文件数据flush到了disk上。fdatasync返回成功后,那么可以认为数据已经写到了磁盘上。像这样的flush的函数还有fsync、sync。

  1. Fsync和fdatasync的区别等同于O_SYNC和O_DSYNC的区别。
  2. Sync函数表示将文件在OS cache中的数据排入写队列,并不确认是否真的写磁盘了,所以sync并不可以靠。

忽略文件打开的过程,通常我们会说“写文件”有两个阶段,一个是调用write我们称为写数据阶段(其实是受open的参数影响),调用fsync(或者fdatasync)我们称为flush阶段。

回到MySQL,参数Innodb_flush_method(Linux)可以设定为:Fdatasync、O_DSYNC、O_DIRECT。我们看看这个三个参数是如何影响程序MySQL对日志和数据文件的操作:

  Open log Flush log Open datafile Flush data
Fdatasync   fsync()   fsync()
O_DSYNC O_SYNC     fsync()
O_DIRECT   fsync() O_DIRECT Fsync()

fdatasync被认为是安全的,因为在MySQL总会调用fsync来flush数据。使用O_DSYNC是有些风险的,有些OS会忽略该参数O_SYNC

我们看到O_DIRECT和fdatasync和很类似,但是它会使用O_DIRECT来打开数据文件。


你可能感兴趣的:(innodb_flush_method 与 File I/O)