Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

存储硬件: NAND Flash / SSD FTL / 写入放大 / NVMe

TL;DR

数据库的 WAL、LSM-Tree 的 compaction、分布式系统的 fsync——所有"持久化"的语义最终都落在 SSD 内部的 NAND flash 上。SSD 不是"快一点的磁盘":它不能覆盖写(只能先擦除后写)、有寿命上限(P/E 次数)、内部还有一层把你完全屏蔽的 FTL(闪存转换层)。这一章把 NAND 物理特性(SLC/MLC/TLC/QLC)、SSD 内部机制(磨损均衡、垃圾回收、写放大、掉电保护)、NVMe 协议(队列对、多命名空间)讲透,并回答数据库工程师最关心的:为什么 LSM 写放大要对着 SSD 写放大一起算

读完应能:

  1. 说出 NAND 的"读页 / 写页 / 擦除块"三粒度,以及 SLC→QLC 的寿命与速度差异。
  2. 解释 FTL 的 L2P 映射、磨损均衡、垃圾回收如何共同决定"写放大系数",并估算 1 次逻辑写实际消耗多少物理写。
  3. 说清 TRIM、OP(预留空间)、掉电保护各自解决什么问题。
  4. 讲出 NVMe 与 AHCI/SCSI 的队列模型差异(多队列、polling、无锁提交/完成)。
  5. 把 SSD 特性翻译成数据库设计决策:WAL 为什么写少量页、LSM 为什么反而被 SSD 喜欢、QLC 时代怎么选盘。

一、HDD vs SSD: 两种完全不同的物理

HDDSSD
随机读寻道 ~10ms~50-100µs(受队列深度影响)
随机写寻道 ~10ms与顺序写接近(FTL 内做批量)
写单元扇区 512B/4K页 4-16KB(最小写粒度)
擦除单元块 4-16MB(最小擦除粒度)
覆盖写直接覆写必须先擦除(无覆盖写)
寿命近乎无限P/E 次数有限

一句话:SSD 把"随机 IO"的物理代价抹平了,但把"寿命 + 空间放大"的账转嫁给了固件和上层软件。

二、NAND flash 物理

NAND 用浮栅/电荷陷阱晶体管存电荷表示 1 位或多位。每单元存几位决定密度、速度、寿命的三角关系:

类型每单元位数电压档数相对寿命相对速度用途
SLC12~10x企业缓存/系统盘
MLC24~4x高端消费/企业
TLC381x中慢主流消费/云
QLC416~0.3x大容量冷数据

干扰与衰减:

  • 写干扰:向一个页编程会扰动同块其他页的电荷;
  • 读干扰:反复读一个页会累积扰动(读多了要先搬移数据再读);
  • 数据保持:电荷随时间泄漏,TLC/QLC 在高温下保持期显著缩短——冷数据盘要定期刷新。

三、SSD 内部: FTL 是那个"看不见的数据库"

3.1 逻辑到物理映射(L2P)

主机看到的是连续逻辑块(LBA),实际写到哪里由 FTL 决定:

主机:   LBA 1000 写入 4KB
FTL:    找空闲页 (可能在任何物理块), 写入, 更新 L2P 表
        (L2P 表本身按 4KB 粒度记录映射, 1TB 盘 ≈ 256M 条)
  • 这就是为什么 SSD 随机写和顺序写性能接近——FTL 把随机逻辑写重排成顺序物理写;
  • L2P 表大,SRAM 放不下,所以有 DRAM 缓存(断电丢)或 HMB/无 DRAM 设计(用主机内存)。

3.2 磨损均衡(Wear Leveling)

同一个块不能反复擦除。FTL 记录每个块的擦除次数,尽量均匀使用:

  • 动态均衡:只在写时选择最年轻块;
  • 静态均衡:把长期不动的冷数据搬到快坏死的块上(让热数据用年轻块)——牺牲一点迁移 IO 换整盘寿命。

3.3 垃圾回收与写入放大

覆盖写 = 把旧页标记无效 + 写新页。无效页要等块整块回收:GC 把块里还有效的页搬走,再擦除整块:

写放大 WA = 实际写入 NAND 的物理字节 / 主机下发的逻辑字节
典型: TLC 盘顺序写 WA≈1.1-1.5, 随机小写 WA 可达 3-10+

影响 WA 的旋钮:

  • OP(Over-Provisioning,预留空间):出厂多留 7%-28% 物理块不暴露给主机。OP 越大,GC 越从容、WA 越低、寿命越长;
  • TRIM:主机告诉 SSD"这些 LBA 数据已删除",FTL 把对应页直接标无效,避免 GC 搬死数据;
  • 写入模式:随机 4K 小写是 WA 噩梦;顺序大块写接近 1。

note

数据库与 WA 直接相关:LSM-Tree 的 compaction 把 N 次小写重写成更少的大写,把 WA 从 3-10 压向 1.x,所以 LSM 天然适合 SSD(见 databases/indexing/lsm.md)。反过来 B+ 树随机页写要靠 FTL 兜底,WA 更高。

3.4 掉电保护

写一半断电,FTL 映射表可能撕裂:

  • 企业盘:带电容(power-loss protection),掉电后靠电容把 DRAM 里未落盘的映射表/数据刷进 NAND;
  • 消费盘:常无保护——这正是"数据落到 page cache 不代表安全"、"必须 fsync"的硬件注脚。

四、NVMe: 为并行而生的协议

NVMe 替代 SATA/AHCI 的关键是队列模型

旧 AHCI:  1 个队列, 深度 32, 命令要敲门铃 + 中断 → 单队列串行
NVMe:     64K 个队列对, 每个队列深 64K
          Submission Queue (主机→盘) / Completion Queue (盘→主机)
          doorbell 写一次, 批量取命令; 支持 polling(无中断)
  • 多核 CPU 每个核自己的队列 → 无锁提交,IOPS 从几万涨到百万级;
  • 中断聚合(interrupt coalescing)+ polling 模式:低延迟场景(数据库 fsync)用 polling 可把尾延迟降一半;
  • 多命名空间(namespace):一块盘切成多个逻辑盘,可独立配额/加密;
  • ZNS(Zoned Namespace):把"顺序写"语义暴露给主机,让数据库/LSM 直接管理块——省掉 FTL 的部分搬运,下一代存储选型点。

五、对上层软件的三条铁律

  1. fsync 是昂贵的:NVMe 单次刷盘 ~10-50µs 但涉及掉电保护语义 + 队列 flush,批量合并 fsync 是 WAL 高吞吐的命门;
  2. 顺序大块 > 随机小块:能批量就批量;O_DIRECT 大 IO + io_uring 多队列是数据库/存储引擎的标准姿势;
  3. 寿命按 TBW 算预算:TLC 盘 TBW 有限,日志类高写负载选企业 TLC/MLC + 大 OP,QLC 只放冷数据。

warning

常见误判:把消费级 NVMe 当企业盘用在高写入日志系统,1-2 年盘就报错掉盘。选盘先看 TBW(总写入字节)与负载的写入速率,而不是顺序读速度。

六、一页速查

粒度:     页(4-16KB, 写) < 块(4-16MB, 擦除); 无覆盖写
寿命:      SLC>MLC>TLC>QLC; TBW 是预算, 写放大决定实际消耗
FTL:      L2P 映射 + 磨损均衡 + GC + TRIM + OP + 掉电保护
写放大:   随机小写 3-10x, 顺序大块 ~1.x, OP/TRIM 压低
NVMe:     64K 队列对 + polling, 百万 IOPS, ZNS 把顺序语义交还主机
数据库:   WAL 批量 fsync; LSM 对 SSD 友好; QLC 只放冷数据

下一篇: 总线与互联: PCIe / CXL / NVLink / RDMA