SSD 与传统磁盘的本质区别
机械硬盘 (HDD) 是”可原地覆盖”的磁介质;而 NAND Flash 不能原地覆盖——写入前必须先擦除,且擦除单位远大于写入单位。这一不对称性塑造了 SSD 的全部工程难题,也直接影响数据库的写入策略。
存储单元:浮栅晶体管
NAND 的每个存储单元是一个浮栅 (Floating Gate) 晶体管,通过俘获电荷表示比特:
控制栅 (Control Gate)
│
┌────┴────┐
│ 浮栅 FG │ ← 注入电子 = 0(阈值电压升高)
│ (电荷) │ ← 放出电子 = 1(阈值电压降低)
└────┬────┘
隧道氧化层
│
衬底 (Substrate)
- 电子注入浮栅(隧穿)→ 阈值电压高 → 记为 0
- 放出电子 → 阈值电压低 → 记为 1
- 电荷可保留多年(但有泄漏,需 ECC 纠错)
SLC / MLC / TLC / QLC:一个单元存 1/2/3/4 比特,密度越高越便宜,但擦写寿命与速度越差。
页、块与读写擦的不对称
NAND 以页 (Page, 4-16KB) 为读写单位,但以块 (Block, 数十至数百页) 为擦除单位:
Block (如 256 页)
┌────┬────┬────┬─────┬────┐
│Page│Page│Page│ ... │Page│ ← 读/写以页为单位
└────┴────┴────┴─────┴────┘
↑ 整块一起擦除
关键约束:
| 操作 | 最小单位 | 速度 |
|---|---|---|
| 读 | 页 | 快 (~100 μs) |
| 写(编程) | 页 | 中 (~200 μs) |
| 擦除 | 块 | 慢 (~2-3 ms) |
不能原地更新:要改某页,必须先把整个块读到缓存,擦除整块,再写回新数据。
FTL:闪存转换层
SSD 内部有控制器运行 FTL (Flash Translation Layer),把操作系统的”逻辑块地址 (LBA)“映射到物理页,并对外伪装成可随机覆盖的块设备:
OS: 写 LBA 100 → 覆盖
│
▼
FTL: 实际写到新物理页 P新,
把旧页 P旧 标记为无效 (invalid)
更新映射表 100 → P新
- 写入放大 (Write Amplification):逻辑写 4KB,物理可能写 16KB+(含块回收)
- 映射表常驻 DRAM(断电靠掉电保护电容刷回)
写放大与垃圾回收
有效页 (valid) 与无效页 (invalid) 混在一个块里。回收必须:
1. 选中有较多无效页的块
2. 把块内有效页 搬移 到别处
3. 整块擦除
4. 标记为可用
回收前: [V][I][V][I][I] V=有效 I=无效
搬移后: [V][V] → 另存
擦除后: [ ][ ][ ][ ][ ] 整块清空,可再写
写放大因子 = 物理写入量 / 主机写入量。随机小写入、填充率过高都会放大写放大,拖慢 SSD 并加速磨损。
磨损均衡 (Wear Leveling)
每个块擦写次数有限(TLC ~1000-3000 次,SLC 数万次)。FTL 通过磨损均衡均衡各块擦写:
- 动态:优先用擦写少的块接收新数据
- 静态:偶尔把”只读冷数据”搬移,让长期不写的块也参与磨损
TRIM 与读取干扰
- TRIM/DISCARD:OS 告知 SSD 某 LBA 已删除,FTL 提前标记无效页,减少后续垃圾回收负担。数据库的
VACUUM/ 文件删除后及时 TRIM 很重要。 - 读取干扰 (Read Disturb):频繁读某页可能干扰同块其他页电荷,控制器会定期重定位。
对数据库的启示
| SSD 特性 | 数据库应对 |
|---|---|
| 顺序写远快于随机写 | WAL 顺序追加;LSM 顺序 Compaction |
| 写放大 | 避免频繁小随机更新;批写 |
| 不能原地更新 | 追加写 / COW(如 WAL、B-tree 的 HOT) |
| TRIM 重要 | 删除后 TRIM;定期 vacuum |
| 磨损有限 | 写密集负载选高耐久盘 (TLC/企业级) |
SSD 让”随机读”不再致命(比 HDD 快 ~100 倍),但随机写仍是其相对弱点——这正是 WAL 与 LSM-Tree 设计的核心动机。
参考
- Understanding SSDs, Chen, Xue, et al.
- OpenSSD: Introduction to NAND Flash
- PostgreSQL WAL Documentation