最近和文件系统内核开发人员做技术交流,对O_DIRECT选项有了新的认识。
Try to minimize cache effects of the I/O to andfromthis file.In general this will degrade performance, but it is useful in special situations, such aswhen applications do their own caching.
然而在源码内,细心的读者可能会产生一些困惑,因为不管参数innodb_flush_method的设置为何值,在刷新脏页时都会调用fsync操作,具体见函数buf_flush_buffered_writes。那么,当用户已经打开文件操作的O_DIRECT标识,为什么还需要进行一次fsync操作确保文件的写入呢?
最早发现这个问题的是Facebook的MySQL团队负责人Mark Calleghan。其在2009年时在MySQL数据库的官方论坛中提交Bug #45892,当年看到此Bug的回复时还是有些疑惑,因为其答复的诸如xfs这类文件系统,有些元数据还需要通过fsync进行刷新。公司同事花花也有问过我同样的问题,不知道是否在做云硬盘时遇到了类似问题。
最近和文件系统内核开发人员做交流,其对ext4的文件系统做了简单的介绍,自己对文件系统有了重新认识,对之前Bug的回复也有了更为清楚的理解。在有些文件系统中,例如ext4、xfs,文件(包括目录,在Linux中所有对象都是文件)都有一个inode与之对应,其保存有两部分的内容,元数据和文件的存储数据。根据wiki的介绍,元数据包含的内容有:
- 文件的字节数
- 文件的权限
- 文件的时间戳
- 链接的数量,即有多少文件指向该inode
- 指向数据块的链接
- etc
可 以发现元数据及其重要的,不仅仅是文件最后修改时间、权限等信息,它还包含有指向存储块的信息。若在数据增长时,元数据没有及时更新,那么同样可能会导致 数据丢失的情况。虽然此时,数据可能在磁盘上,但文件不知道那些块也是其组成部门。可以看到,这也是MySQL官网对上述Bug中的回复:
For example,if a file grows while O_DIRECT is enabled it will still write to the new part of the file, however since the metadata doesn't reflect the new size of the file the tail portion can be lost in the event of a crash.
inode中的元数据是保存在inode cache中,inode的保存文件的数据是保存在操作系统缓存中(若未开启O_DIRECT标识)。读者可以观察下图Linux文件系统的实现方式:
![InnoDB O_DIRECT选项漫谈(一) - insidemysql - Inside MySQL](http://img0.ph.126.net/WdTZV0wOozqF6emGqd24Yg==/1445092530532537779.jpg)
fsync操作会同步上图中的Inode cache,Buffer cache(也就是操作系统缓存),Directory cache。 因此这就是为什么InnoDB存储引擎即使在文件打开时加上O_DIRECT标识,刷新脏页依然需要fsync操作。这是因为O_DIRECT标识只是忽 略了图中Buffe cache。刷新文件的另一个函数fdatasync,其仅刷新buffer cache的内容到磁盘,因此比fsync有更好的性能,但是存在数据丢失的风险。
若用户想通过O_DIRECT写入文件,但避免可能存在的潜在风险时,可以再加上O_SYNC标识,此时写入实际变为了一个同步写(synchronous I/O)操作,因此不再需要额外的fsync操作。见man手册中的说明:
The O_DIRECT flag on its own makes an effort to transfer data synchronously, but does not give the guarantees of the O_SYNC flag that data and necessary metadata are transferred.To guarantee synchronous I/O, O_SYNC must be used in addition to O_DIRECT.
未完待续......
MySQL5.6中增加了一个选项:O_DIRECT_NO_FSYNC