数据库系统总会涉及非易失性存储,我们需要知道一个典型的计算机系统是如何进行存储管理的。
本文回顾硬盘的物理特性,硬盘的主要性能指标,以及操作是如何进行硬盘 I/O 性能优化的,最后参考开源系统来讨论如何根据硬盘特性进行系统设计。
一、磁盘IO背景知识


二、常见的硬盘 I/O 优化
追加写:同时根据磁盘顺序读写快、随机读写慢的特点,尽量做追加写;
使用用较大的文件:通过优化文件的大小提高读写性能
IO调度策略优化:根据IO的特点选择合适的IO调度策略
零拷贝技术:通过用户态直接IO或者减少数据拷贝次数达到
将小文件合并为大文件
优化元数据存储和管理
Google File System和 Facebook Haystack是两个典型的案例:
GFS 选择了当时看来相当大的 64M 作为数据存储的基本单位,就是为了减少大量元数据;
Facebook Haystack 同样将小文件集合成大文件来减少了元数据数目;同时精简元数据,去掉一切 Facebook 场景中不需要的元数据,压缩元信息到足够小并全部加载到内存中,避免请求 inode 带来的开销。
CFQ,完全公平队列调度。早期Linux 内核的默认调度算法,它给每个进程分配一个调度队列,默认以时间片和请求数限定的方式分配IO资源,以此保证每个进程的 IO 资源占用是公平的。这个算法在IO压力大,且IO主要集中在某几个进程的时候,性能不太友好。
DeadLine,最终期限调度。这个调度算法维护了4个队列,读队列,写队列,超时读队列和超时写队列。当内核收到一个新请求时,如果能合并就合并,如果不能合并,就会尝试排序。如果既不能合并,也没有合适的位置插入,就放到读或写队列的最后。一定时间后, I/O调度器会将读或写队列的请求分别放到超时读队列或者超时写队列。这个算法并不限制每个进程的IO资源,适合IO压力大且IO集中在某几个进程的场景,比如大数据、数据库使用HDD磁盘的场景。
NOOP,也叫NONE,是一种简单的FIFO调度策略。因为固态硬盘支持随机读写,所以固态硬盘可以选择这种最简单的调度策略,性能最好。
用户态直接 I/O:应用程序可以直接访问硬件存储,操作系统内核只是辅助数据传输。这种方式依旧存在用户空间和内核空间的上下文切换,硬件上的数据直接拷贝至了用户空间,不经过内核空间。因此,直接 I/O 不存在内核空间缓冲区和用户空间缓冲区之间的数据拷贝。
减少数据拷贝次数:在数据传输过程中,避免数据在用户空间缓冲区和系统内核空间缓冲区之间的CPU拷贝,以及数据在系统内核空间内的CPU拷贝,这也是当前主流零拷贝技术的实现思路。
写时复制技术:写时复制指的是当多个进程共享同一块数据时,如果其中一个进程需要对这份数据进行修改,那么将其拷贝到自己的进程地址空间中,如果只是数据读取操作则不需要进行拷贝操作。
参考资料:
https://www.jianshu.com/p/fad3339e3448
https://juejin.cn/post/6844903949359644680
https://gist.github.com/jboner/284183

SHAREit技术团队
扫码 ╳ 关注我们
微信号|SHAREitTech