编程语言研究书库

闪存盘与数据完整性 — 新硬件的新脾气,和「盘会说谎」的应对

18 闪存盘与数据完整性 — 新硬件的新脾气,和「盘会说谎」的应对

1. 这一章讲什么

持久化部分的最后两块拼图。第一块:SSD——闪存没有机械臂, 随机读快几百倍,但写要先擦除整块(毫秒级)且每块只能擦写 约一万次。把这样的芯片包装成「一块普通盘」卖给世界, 就是闪存转换层(FTL)的工程;而它的解法,你会认出是 LFS 的思想。 第二块:数据完整性——补 RAID 章留的尾巴:真实世界里盘很少 「干脆死掉」,更多是静默说瞎话;检测靠校验和,恢复靠冗余, 原则靠端到端。

2. 顶层全景

  闪存的三条物理约束:
    读页:快(数十 µs,随机=顺序)
    写页:只能 1→0,且必须先擦除整块(128–256KB,毫秒级)
    寿命:反复 P/E 累积残电荷 → 块磨损(MLC ≈ 10,000 次)
        │
  FTL 的三件套(把怪脾气藏成普通块设备):
    日志式映射:写永远追加到当前块,映射表记 逻辑块 → 物理页
    垃圾回收:整段搬活块、回收死块(写放大的来源)
    磨损均衡:让全盘的块均匀老化
        │
  数据完整性:
    fail-partial 模型:整盘坏死之外,还有 LSE(读时报错,好办)
    和静默损坏(返回错数据不吭声,难办)
    检测 = 校验和(XOR/加法/Fletcher/CRC 的强度-速度权衡)
    恢复 = 冗余(镜像/RAID-DP 双奇偶);原则 = 端到端

图说:上半是「新硬件的新约束与它的包装层」,下半是「旧冗余观 不够用了,补上检测这一半」。

3. 核心原理

3.1 闪存的基本脾气(主线例子:改一页,赔一块)

组织结构:块(block / 擦除块,128–256KB)> 页(page,数 KB)。 三个操作(书里的性能表)1:

操作粒度耗时(SLC 例)说明
读页~25 µs随机地址一样快——随机访问设备
编程(program)页200–300 µs只能把 1 写成 0
擦除块1.5–2 ms整块清成 1,毁掉块内所有数据

图说:注意术语陷阱——闪存的「块/页」和磁盘的「块」、虚拟内存的 「页」完全不是一个东西,各域有各域的叫法2。

改一页的完整代价(书里的 4 页小例子,数值照抄): 块里 4 页都已编程(VALID),要把 Page 0 从 00011000 改成 00000011——先擦整块,四页全变 11111111(ERASED), 再编程 Page 0;Page 1–3 的旧内容全部陪葬3。 所以任何「覆写」之前,必须先把块里还在用的页搬到别处—— 这一条物理约束,决定了 FTL 的全部形状。

页状态机也值得一提:INVALID →(擦除)ERASED →(编程)VALID; 已编程的页不能再编程,唯一的改法是擦整块4。

寿命与干扰:MLC 块标称 10,000 次 P/E,SLC 100,000 次 (实测往往更长);另有读/编程干扰(碰一页,邻居页位翻转)5。 所以「写性能」和「活得久」是 SSD 设计的两个主战场。

3.2 FTL:日志式映射如何把怪脾气藏成一块盘

反面教材:直接映射(逻辑页 N = 物理页 N)。 每次写:读整块 → 擦整块 → 把旧页+新页一起写回。 写放大正比于块内页数,比机械盘还慢;且热点逻辑块把对应的 物理块反复擦写,很快磨死6。性能和寿命双输。

正解:日志式 FTL。写逻辑块 N 时,不找「N 的老位置」, 而是追加到当前正在写的块的下一个空页;内存里(并有持久形式) 的映射表记 N → 最新物理地址7。主线例子(书里的玩具盘: 16KB 块 ÷ 4KB 页;客户写 逻辑块 100=a1、101=a2、2000=b1、 2001=b2):FTL 先擦块 0,按页序把 a1、a2 写进页 0、1, 再擦块 1 写 b1、b2;映射表相应记下 100→块0页0 等8。 按页序写还有个附带好处:减轻编程干扰9。

垃圾回收与写放大。覆写产生死页,攒多了就要回收: 读 M 个旧段里的活块、压进 N 个新段(M>N)、擦掉旧段。 写放大 = FTL 发给闪存的总写入 ÷ 客户请求的写入—— 段里活块越多,搬运越亏;接近满盘时写放大急剧恶化, 这是「SSD 要留空闲」和「TRIM 命令告诉它哪些块已删」的由来10。

磨损均衡:映射本来就是间接层,FTL 可以周期性把 冷数据搬去磨损重的块、热数据挪到磨损轻的块, 让所有块均匀老化——直接映射给不了这个自由11。

一个呼应:#17 章的 LFS 在文件系统层做了同样三件事 (日志写、垃圾回收、间接定位);FTL 在设备层又做了一遍。 闪存 SSD 其实是「LFS 哲学在硅片里的实现」,只是客户看不见 ——这就是向后兼容的力量与代价:接口不变,两侧各自进化, 接口本身却成了下一代的束缚(书里引 ZFS 为例)12。

3.3 数据完整性:当盘开始说瞎话

RAID 章假设 fail-stop(盘非好即坏,坏了立刻知道)。 现实是 fail-partial13:两类部分故障——

  • 潜伏扇区错误(LSE):扇区物理损坏,读时报错。 好办:检测免费,冗余直接顶上(镜像读另一份;奇偶组重算)。 但有个组合拳要防:整盘重建期间撞上另一块盘的 LSE, 重建就完不成——NetApp RAID-DP 的答案是双奇偶, 用一块盘的空间换重建期的第二条命14。
  • 静默损坏(corruption):固件把块写错位置、总线传错数据, 盘自己 ECC 全过、高高兴兴返回错数据。难办:检测是问题。

检测靠校验和(checksum):对 4KB 块算一个小摘要(4–8 字节), 随数据存下,读时重算比对15。函数谱系是强度-速度的权衡 (没有免费午餐)16:

校验和怎么算强度
XOR各 4 字节块异或弱:同位置双比特翻转漏检(书里的 16 字节例得 0x201b9403)
加法分块相加忽略溢出快;对移位弱
Fletcher两个累积字节 s1、s2(mod 255)接近 CRC
CRC把数据当大二进制数除约定除数取余常用最强

图说:再强也有碰撞(不同数据同摘要)——摘要一定比原文小, 选对函数是在漏检率和算力间取点17。

布局的麻烦:校验和很小、盘按 512 字节写,厂商解法是把扇区 格式化成 520 字节(+8 存校验和);没有这功能的盘,文件系统得把 n 个校验和挤进一个扇区、再跟 n 个数据块——改一个数据块 要读校验和扇区、改它、连数据一起写回(1 读 2 写)18。

端到端视角收尾(补充,不在书里,是我们自己的综合): 下层(总线、盘内缓存、固件)都可能弄坏数据,只有端点(写数据的人、读数据的人)才能真正为完整性定责——中间各层的校验只是优化,不能替代端点的校验与恢复决策。原书以 ZFS 为代表点到「灵活端到端数据完整性」的方向(见该章参考文献 [Z+13]19)。所以可靠的存储栈是:冗余(恢复)+ 校验和(检测)+ 端点定责。

4. 作者的判断与证据

书里给了证据的判断:

  • 闪存三操作的延迟表引自公开测量1;磨损寿命引厂商标称 与 [BD10] 的实测修正5;
  • LSE/静默损坏的频率来自对 150 万块盘、约 3 年的两项研究 (便宜盘 LSE 9.4%、损坏 0.5%;贵盘低约一个数量级)13;
  • 校验和的强度对比引 Fletcher 原文与 CRC 文献16。

作者的判断(立场):

  • 「接口稳定使能两侧创新,但也可能成为下一代的束缚」—— 对 SSD 伪装成块设备这件事,书里态度是双面的12。
  • 「想建可靠的存储系统,必须同时有检测和恢复两种机器」, 是这章的判词13。

5. 边界与局限

  • FTL 是商业机密,书里讲的是原理级模型(Agrawal 等的研究是 最深入的外部刻画);各家实现(静态/动态磨损均衡、贪心/成本感知的 回收策略)差异很大。
  • 本章闪存叙述以 2010 年代中期的 SLC/MLC/TLC 为基准; QLC/PLC 与 3D NAND 已改变具体数字,但「擦整块才能写、 会磨损」两条物理约束不变。
  • 数据完整性一章没展开「纠删码」(erasure coding)—— 数据中心里替代 RAID 的更大粒度冗余方案,属于下一层读物。

6. 可带走的

  1. 闪存三操作(读页/编程页/擦块)的粒度与成本差两个数量级; 一切 SSD 设计都是绕着「擦除」转。
  2. 「写 = 先搬家、再擦块、再追加」→ FTL 的日志结构是物理约束 的必然,不是风格选择。
  3. 写放大是 FTL 的头号指标;GC 策略、OP(预留空间)、TRIM 都围着它转。
  4. 磨损均衡能存在,是因为映射表这层间接——间接层换来调度自由, 这个模式在虚拟内存、LFS、FTL 里重复出现。
  5. 可靠的冗余设计要按 fail-partial 想:检测和恢复是两种 必须分别建设的机器;只囤冗余没有校验和,等于对瞎话照单全收。
  6. 端到端原则:信任边界画在端点上,中间层的一切「保证」 都只是性能优化。

7. 原文地图

主题原书章原文位置
闪存单元 SLC/MLC/TLC44. Flash-based SSDstext/46-ch44-44-flash-based-ssds.txt:32(搜「triple-level」)
块与页的组织44. Flash-based SSDstext/46-ch44-44-flash-based-ssds.txt:55(搜「erase blocks」)
三个操作与状态机44. Flash-based SSDstext/46-ch44-44-flash-based-ssds.txt:79(搜「Basic Flash Operations」)
改一页赔一块的例子44. Flash-based SSDstext/46-ch44-44-flash-based-ssds.txt:127(搜「Detailed Example」)
操作延迟表44. Flash-based SSDstext/46-ch44-44-flash-based-ssds.txt:171(搜「SLC」)
磨损与干扰44. Flash-based SSDstext/46-ch44-44-flash-based-ssds.txt:200(搜「10,000」)
FTL 定义与写放大44. Flash-based SSDstext/46-ch44-44-flash-based-ssds.txt:246(搜「flash translation layer」)
直接映射之坏44. Flash-based SSDstext/46-ch44-44-flash-based-ssds.txt:283(搜「Bad Approach」)
日志式 FTL 的例子44. Flash-based SSDstext/46-ch44-44-flash-based-ssds.txt:327(搜「Write(100)」)
磨损均衡44. Flash-based SSDstext/46-ch44-44-flash-based-ssds.txt:276(搜「wear leveling」)
fail-partial 模型与两项研究45. Data Integrity and Protectiontext/47-ch45-45-data-integrity-and-protection.txt:55(搜「fail-partial」)与:32(搜「9.40%」)
LSE 的处理与 RAID-DP45. Data Integrity and Protectiontext/47-ch45-45-data-integrity-and-protection.txt:97(搜「Latent Sector」)与:127(搜「RAID-DP」)
校验和定义45. Data Integrity and Protectiontext/47-ch45-45-data-integrity-and-protection.txt:136(搜「checksum」)
XOR 校验和算例45. Data Integrity and Protectiontext/47-ch45-45-data-integrity-and-protection.txt:198(搜「0x201b9403」)
Fletcher 与 CRC45. Data Integrity and Protectiontext/47-ch45-45-data-integrity-and-protection.txt:211(搜「Fletcher」)与:218(搜「CRC」)
校验和布局(520 字节扇区)45. Data Integrity and Protectiontext/47-ch45-45-data-integrity-and-protection.txt:232(搜「layout」)
端到端原则45. Data Integrity and Protectiontext/47-ch45-45-data-integrity-and-protection.txt:485(搜「end-to-end」)

Footnotes

  1. 出处:「44. Flash-based SSDs」第 79–174 段(text/46-ch44-44-flash-based-ssds.txt:171,搜「SLC」)。图 44.2:SLC 读 25µs/编程 200–300µs/擦除 1.5–2ms;MLC 约 50/600–900/3000µs;TLC 约 75/900–1350/4500µs。 ↩ ↩2

  2. 出处:「44. Flash-based SSDs」第 34–44 段(text/46-ch44-44-flash-based-ssds.txt:34,搜「TERMINOLOGY」)。 ↩

  3. 出处:「44. Flash-based SSDs」第 127–152 段(text/46-ch44-44-flash-based-ssds.txt:132,搜「00011000」)。 ↩

  4. 出处:「44. Flash-based SSDs」第 105–123 段(text/46-ch44-44-flash-based-ssds.txt:106,搜「INVALID」)。 ↩

  5. 出处:「44. Flash-based SSDs」第 190–210 段(text/46-ch44-44-flash-based-ssds.txt:200,搜「10,000」)。 ↩ ↩2

  6. 出处:「44. Flash-based SSDs」第 283–304 段(text/46-ch44-44-flash-based-ssds.txt:300,搜「direct mapped」)。 ↩

  7. 出处:「44. Flash-based SSDs」第 309–317 段(text/46-ch44-44-flash-based-ssds.txt:310,搜「log structured」)。 ↩

  8. 出处:「44. Flash-based SSDs」第 318–360 段(text/46-ch44-44-flash-based-ssds.txt:327,搜「Write(100)」)。 ↩

  9. 出处:「44. Flash-based SSDs」第 278–281 段(text/46-ch44-44-flash-based-ssds.txt:280,搜「low page to high page」)。 ↩

  10. 出处:「44. Flash-based SSDs」第 257–260 段(text/46-ch44-44-flash-based-ssds.txt:270,搜「write amplification」)。 ↩

  11. 出处:「44. Flash-based SSDs」第 271–277 段(text/46-ch44-44-flash-based-ssds.txt:276,搜「wear leveling」)。 ↩

  12. 出处:「44. Flash-based SSDs」第 215–228 段(text/46-ch44-44-flash-based-ssds.txt:215,搜「BACKWARDS COMPATIBILITY」)。 ↩ ↩2

  13. 出处:「45. Data Integrity and Protection」第 30–95 段(text/47-ch45-45-data-integrity-and-protection.txt:55,搜「fail-partial」)。数据:约 3 年、150 万盘;便宜盘 LSE 9.4%、corruption 0.5%;贵盘 1.4%、0.05%。 ↩ ↩2 ↩3

  14. 出处:「45. Data Integrity and Protection」第 119–134 段(text/47-ch45-45-data-integrity-and-protection.txt:128,搜「RAID-DP」)。 ↩

  15. 出处:「45. Data Integrity and Protection」第 136–160 段(text/47-ch45-45-data-integrity-and-protection.txt:152,搜「checksum」)。 ↩

  16. 出处:「45. Data Integrity and Protection」第 163–230 段(text/47-ch45-45-data-integrity-and-protection.txt:171,搜「Checksum Functions」)。 ↩ ↩2

  17. 出处:「45. Data Integrity and Protection」第 226–230 段(text/47-ch45-45-data-integrity-and-protection.txt:227,搜「collision」)。 ↩

  18. 出处:「45. Data Integrity and Protection」第 232–269 段(text/47-ch45-45-data-integrity-and-protection.txt:250,搜「520-byte」)。原文:厂商把扇区格式化为 520 字节,多出的 8 字节存校验和;无此功能时文件系统把 n 个校验和挤进一个扇区,改一块需 1 读 2 写。 ↩

  19. 补充(不在书里,来自通用知识):端到端论证是 Saltzer 等人在 1981 年提出的系统设计原则。原书第 45 章参考文献 [Z+13](text/47-ch45-45-data-integrity-and-protection.txt:485,搜「Zettabyte Reliability」)指向 ZFS 的「灵活端到端数据完整性」工作。 ↩

On this page