日志文件系统
本章为进阶内容,零基础读者可以跳过,不影响后续阅读。
复习
- 缓冲与页面缓存:内存怎样减少缓慢的设备访问
- 共享内存与内存映射:把文件访问与共享页面统一起来
- 断电时写到一半怎么办:崩溃一致性与写入顺序
TL;DR
- 直接改真实结构,崩溃就可能留下“半成品”
- 日志文件系统:先把“打算怎么改”写进日志,再动真格
- 日志写完整了,才算“提交”;之后即使崩溃也能照日志重做
- 先记意图、再做修改,用一份日志换来崩溃一致性
正文
上一章留下了难题:一次修改牵动多处,崩溃卡在中间就会不一致。这一章看一个漂亮的解法——日志(journal)。
先把“打算做什么”记下来
日志的思路,和记账很像:动手之前,先写下将要做的每一笔。
要修改文件系统时,不直接去改真实结构,而是:
- 写日志:把这次要做的所有修改(比如“更新 inode、更新空闲位图”),先完整写进一块专门的日志区域
- 提交:等日志全部写完、确认无误,再打上一个“已提交”的标记
- 检查点:之后才把这些修改,真正应用到磁盘上的正式结构
① 写日志(记录所有改动)
② 提交标记
③ 应用改动到真实结构(检查点)
崩溃了怎么办
关键在于:日志只要完整写完并提交,这次修改就算数;否则就算没发生。
崩溃后重启,系统只看日志:
- 日志里有“已提交”的记录 → 把里面的修改重做一遍,保证做完
- 日志里没有提交标记 → 认为这次改动没发生,丢弃
于是,无论崩溃发生在哪一刻,最终结果都只有两种:要么整个修改都生效,要么整个都不生效——不再有“改了一半”的中间状态。日志,把“多步的修改”包装成了一个原子的整体。
代价与取舍
日志不是免费的:
- 写两遍:同样一份修改,先写日志、再写正式结构,慢
- 占空间:日志区域本身要占磁盘
但这些代价换来了崩溃后的可靠恢复。也正因为如此,现代主流文件系统大多采用了日志(或类似的写时复制机制)。结合上一章的“写入顺序”,日志进一步保证了:先有完整的意图,再有真实的动作。
回过头看,这一路从“怎么放数据”到“崩溃也不乱”,文件系统把磁盘这个又慢又容易出事的部件,收拾成了一个可靠、好用、有名字、能共享的存储世界。
思考题
日志文件系统要“写两遍”,性能上明明是亏的,为什么还值得这么做?它到底买到了什么?
小结
知识点
- 日志:先记录修改意图,再应用到真实结构
- 写日志、提交、检查点三个步骤
- 崩溃后按日志重做或丢弃,保证原子性
- 代价是写两遍,收益是崩溃一致性
参考资料
- Wikipedia(zh):日志文件系统:journaling file system
- Wikipedia(zh):预写式日志:write-ahead logging
思考题答案(仅供参考)
“写两遍”亏的是性能,买到的是崩溃一致性:无论何时断电,文件系统的结构都不会出现自相矛盾的“半成品”。对存储系统来说,数据可靠往往比快一点更重要——丢一次文件、坏一次结构,代价远大于平时多写一遍。而且很多优化可以把这份开销压小。所以这是一笔“用一点性能换可靠”的划算买卖,正如前面缓存是“用空间换时间”——工程里到处都是这样的取舍。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪