之前在研究MySQL的一个参数innodb_flush_method时,就涉及到了fsync/fdatasync这些系统调用[system call](什么是系统调用?它与库函数的区别在哪?参见这里)。接下来就简单的分析一下sync/fsync/fdatasync的区别。
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()负责将这些写物理设备的请求放入写队列,但是不一定写真正被完成了。
(2)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等等。
(3)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对大量随即读写有效率提升,顺序读写则会下降。所以根据自己的需求来定,不过如果你的MySQL用在是OLTP上,基本上选择O_DIRECT没错。