在本文中我将引用五篇有关disk io 的好文章来与读者一起学习探讨关于磁盘IO的事。我大概会提取一些关键点,如果你不太理解或想要深入,不要错过那些链接哦。当然任何文章都不要盲信,不肯定时就要多方论证。
on disk io, part 1
network io 被关注得多,disk io关注得少,部分原因在于network io 有许多特性和不同的实现,而disk(或file system) io只是很少一部分工具子集
sector是块设备的最小传输单元;文件系统最小可寻址单元是block,block是相邻的sector的组合;IO是虚拟内存来操作,它会缓存block在buffer中,虚拟内存操作的是page,它会被映射到block (https://superuser.com/a/350499 这个答案解释得不错,文件系统的block理解为logical block)
读时先查看page cache,如果没有则page in;写时先写到page cache,等合适时再flush
关于DIRECT IO
有些时候不想通过page cache写disk,可能出于效率原因,可以通过指定O_DIRECT来绕过page cache直接写块设备。设计合理的话可以提供更加细粒度的IO控制,应用端常需有自己的cache layer
我详细查看了作者引用的关于Linus在linux kernel新闻组的回复,他说:
- O_DIRECT特别适用于诸如database的应用,若能支持异步则会更好
- Linus认为O_DIRECT接口设计很糟糕,提供了一种比它更好的two-phases way(顺便吐槽了一下database设计者没有好的设计品味)
- 使用AIO是非常好的,但它只是为了掩盖糟糕的设计;倘若接口设计良好不用AIO也是可以的
像mysql, postgresql也是使用了O_DIRECT的,比如pg用于写WAL;同时开启O_DIRECT和page cache当然是不推荐的;
如果要进行direct IO,则需保证io starting address 和io size必须与block大小保持对齐; 无论是否使用direct IO,保持读写时的地址和大小对齐是个好习惯,减少kernel的额外工作
底下有评论说:
对于压缩和序列化操作,用direct io 和user caching要比OS本身的page cache要好
buffer cache和page cache不是一回事 ? 这还有待资料验证
关于nonblock
O_NONBLOCK选项不适用于regular filesNon-blocking I/O with regular files,因为文件系统操作本身就是非阻塞的,所以像select,poll也不适合监控普通文件状态
其它参考
Ensuring data reaches disk
fflush是针对于FILE*,将数据从用户层推到OS层,fsync是针对于fd的,将数据推到持久设备上
/dev/raw设备的访问默认是O_DIRECT
O_SYNC/O_DSYNC还会同步写文件的metadata ?
对于新创建的文件,fsync不仅仅是针对于文件还针对于存放这个文件的目录;
为避免覆盖旧数据系统故障导致数据丢失,可以先写temp file,fsync后,再rename,再fsync directory
write/fflush等调用可能并不报IO error,因为数据还在page cache,但fsync,msync,close时则会出现io error,此时对这些调用的返回值是有必要的;
一些文件系统支持在mount指定是否要flush的选项;
**评论值得一看**
part 2, more flavours of io
mmap
文件内容不是立即调入内存的,采用的是延迟加载的形式,因此第一次访问时会有page fault. 使用MAP_POPULATE
可强制read ahead
内存映射是通过page cache来实现;
对mmapped 文件的随机访问就和普通指针一样,只不过没有lseek操作
mmap的缺陷:
- 内核要维护相应的数据结构
- mapped files大小有限制
此答案建议何时用mmap何时用read:
如果要随机访问,有保留很长时间,用mmap,如果是顺序读取,读完了丢弃使用read;最后谁用起来简单用谁
page cache优化
fadvise
可提供kernel合适的建议,以提高性能。比如如果要顺序读取文件,可以在真正读取之前用它告诉kernel 提前预读;
mlock
可以锁住page,但要慎用以防耗尽内存
aio
glibc并未shipped,需单独安装libaio才能使用;
与O_DIRECT配合使用,不支持buffered io
与posix aio不是一回事,后者是user space aio;
linux aio example
vectored io
可以读small chunks, 减少了系统调用数,且保持了读写原子性;
很少通用型数据库使用vectored io,毕竟它们要处理很多文件,关注延时,cache block-wise,而分析型或列式数据库比较适合使用vectored io,可以并行读入大量数据
目前的方式都有其内在的模式,比如使用O_DIRECT你可能要创建buffer cache,使用page cache需要用到fadvise,使用aio需要用到future-like的接口(指的就是epoll 的回调函数在事件ready后被调用)
On Disk IO, Part 3: LSM Trees
immutable的好处在于不用为数据保留额外空间以防更新成更大的数据;减少了竞争与锁;
immutable/mutable都需要做housekeeping,immutable需要跟踪生成的文件定期做merge,mutable需要减少碎片化
各种DB在LSM的实现上有相似点,都有sorted string table;
sstable就是一个持久化的、有序的、不可变的数据结构,分为data区和索引区;
写都先到内存中的数据结构如skiplist/b-tree,到了一定大小或一定时间就flush到磁盘上成为sstable;
前面的值被后面的update所删除或者写入了新值,称old value is shadowed by new value;
LSM这种设计,在高写负载情况下是可能有高延时的;CPU和磁盘IO可能被Flush,compaction所占满;
有写放大现象;
part 4, b-tree
关于b-tree笔者已经比较熟悉了,但这篇文章还是贴出来,人家都讲得挺好,就不多说了。推荐这篇论文