首页   快速返回

Linux IO sync fsync与fdatasync     所属分类 architecture
缓冲区  写队列 
延迟写(delayed write)
减少磁盘读写次数 
机器故障导致数据丢失
为保证 磁盘文件与缓冲区高速缓存 数据一致性 UNIX系统提供了sync、fsync和fdatasync三个函数

sync 只是将所有修改过的块缓冲区排入写队列,然后就返回,并不等待实际写磁盘操作结束。
通常称为update的系统守护进程会周期性地(一般每隔30秒)调用sync函数 , 定期flush内核的块缓冲区。


fsync 只对文件描述符对应的文件起作用 ,等待写磁盘操作结束,然后返回 ,确保将修改过的块立即写到磁盘上。
除数据外,fsync还会同步更新文件的属性。

fdatasync  与fsync类似 ,只影响文件的数据部分。 

fsync不但更新数据,还会更新元数据——文件的属性,但fdatasync仅更新数据

一般情况下,对硬盘(或者其他持久存储设备)文件的write操作,
更新的只是内存中的页缓存(page cache),而脏页面不会立即更新到硬盘中,而是由操作系统统一调度,
如由专门的flusher内核线程在满足一定条件时(如一定时间间隔、内存中的脏页达到一定比例)内将脏页面同步到硬盘上(放入设备的IO请求队列)。

write调用不会等到硬盘IO完成之后才返回,如果OS在write调用之后、硬盘同步之前崩溃,则数据可能丢失。

#include 
int fsync(int fd);

fsync 确保文件fd所有已修改的内容已经正确同步到硬盘上,该调用会阻塞等待直到设备报告IO完成。


内存映射文件的方式进行文件IO
使用mmap,将文件的page cache直接映射到进程的地址空间,通过写内存的方式修改文件

#incude 
int msync(void *addr, size_t length, int flags)

msync需要指定同步的地址区间,如此细粒度的控制似乎比fsync更加高效(因为应用程序通常知道自己的脏页位置),
但实际上(Linux)kernel中有着十分高效的数据结构,能够很快地找出文件的脏页,使得fsync只会同步文件的修改内容。

fsync性能问题,与fdatasync

fsync不但同步文件的修改内容(脏页),还会同步文件的描述信息(metadata,包括size、访问时间st_atime & st_mtime等等),
因为文件的数据和metadata通常存在硬盘的不同地方,因此fsync至少需要两次IO写操作

Unfortunately fsync() will alwaysalways initialize two write operations: 
one for the newly written data and another one in order to update the modification time stored in the inode. 
If the modification time is not a part of the transaction concept fdatasync() can be used to avoid unnecessary inode disk write operations.
 

当前硬盘驱动的平均寻道时间(Average seek time)大约是3~15ms,
7200RPM硬盘的平均旋转延迟(Average rotational latency)大约为4ms,因此一次IO操作的耗时大约为10ms左右。

#include 
int fdatasync(int fd);

fdatasync的功能与fsync类似,但是仅仅在必要的情况下才会同步metadata,因此可以减少一次IO写操作。

fdatasync does not flush modified metadata unless that metadata is needed in order to allow a subsequent data retrieval to be corretly handled.


文件的尺寸(st_size)如果变化,是需要立即同步的,否则OS一旦崩溃,即使文件的数据部分已同步,由于metadata没有同步,依然读不到修改的内容。
而最后访问时间(atime)/修改时间(mtime)是不需要每次都同步的,只要应用程序对这两个时间戳没有苛刻的要求,基本无伤大雅。
 

open 的参数O_SYNC/O_DSYNC有着和fsync/fdatasync类似的语义:使每次write都会阻塞等待硬盘IO完成。

使用fdatasync优化日志同步


Berkeley DB ,如果开启了AUTO_COMMIT(所有独立的写操作自动具有事务语义)并使用默认的同步级别(日志完全同步到硬盘才返回),
写一条记录的耗时大约为5~10ms级别,基本和一次IO操作(10ms)的耗时相同。

Berkeley DB 对日志文件的处理

1.每个log文件固定为10MB大小,从1开始编号,名称格式为“log.%010d"
2.每次log文件创建时,先写文件的最后1个page,将log文件扩展为10MB大小
3.向log文件中追加记录时,由于文件的尺寸不发生变化,使用fdatasync可以大大优化写log的效率
4.如果一个log文件写满了,则新建一个log文件,只有一次同步metadata的开销

上一篇     下一篇
hive四种存储格式

java版hyperLogLog

磁盘io与直接io

sendfile 零拷贝机制

区块链简介

文字与货币