存储知识点全景

发布于 2026-09-28

目录

第一章 全景图:一条 IO 的旅程与词汇分层

1.1 先建立一张地图:存储金字塔

计算机里所有能放数据的地方,按”离 CPU 多近、多快、多贵、多持久”排成一个金字塔:

寄存器        ~1ns        几十字节
CPU L1/L2/L3  1~30ns      几 MB ~ 几百 MB
内存 DRAM     ~100ns      几十~几百 GB,~几十到几百 GB/s,断电即丢
────────────────────── 以下是本系列说的"存储" ──────────────────────
NVMe SSD      ~100us      微秒级延迟,单盘几 GB/s,断电保留
SATA SSD      ~100us      同样是闪存,但被接口卡住
HDD 机械盘    ~10ms       毫秒级(一次寻道顶 10 万次内存访问)
磁带/冷归档   ~秒~分钟    最便宜,用于归档

用数据库的眼光看这张图,立刻能读出三件事:

  1. 内存和 SSD 差 1000 倍延迟——这就是 buffer pool / shared buffer 存在的全部理由:哪怕 99% 命中率,剩下 1% 落盘都会拖垮整体 RT。
  2. SSD 和 HDD 差 100 倍延迟——B+ 树”减少随机 IO”的设计哲学,本质是在为 HDD 的 10ms 寻道赎罪。你学过的很多”数据库常识”,其实是”HDD 时代的常识”(第 11 章展开)。
  3. 断电是否丢数据是内存和存储的分界线——所以数据库才需要 WAL + fsync 这套”把内存里的修改钉到持久介质”的仪式。

1.2 词汇分层表:每个词都住在某一层

社区讨论里 NVMe、PCIe、RDMA、dist 满天飞,其实它们分别住在不同的层。一条 IO 从应用出发的完整路径是:

应用程序(MySQL / KVCache store / 你的代码)
   ↓ 系统调用 read/write/io_uring
Linux 内核 IO 栈(VFS → 页缓存 → 文件系统 → 块层)
   ↓
协议(NVMe / SATA / SAS)     ← "盘说什么方言"
   ↓
总线(PCIe / SATA 线缆)      ← "方言跑在哪条路上,路有限速"
   ↓
介质(NAND 闪存 / 磁盘片)    ← "数据物理上怎么存"

关键认知:每一层都有自己的吞吐天花板,所以任何性能数字都必须说明”测在哪一层”。盘的 spec 数字、fio 裸盘数字、应用层读到的数字,可以差好几倍。这就是别人质疑”40GB/s 真的假的”的技术根源。

词汇住在哪层一句话解释数据库类比
HDD / SSD / NAND介质数据物理存哪、多快—
NAND page/block介质内部结构闪存的最小读/擦单位类比页和区段
FTL盘内固件闪存翻译层,盘里的”迷你 LSM”LSM 引擎
NVMe协议为闪存设计的高速通信方言专为 MySQL 优化的 wire protocol
AHCI旧协议SATA 时代的方言,单队列,为机械盘设计老的、单请求排队的协议
PCIe总线盘插的高速通道,版本决定限速机房网线带宽
SATA / SAS总线+协议旧接口,SATA 上限 0.6GB/s老网卡
VFS / 页缓存 / 块层内核Linux 管文件和 IO 的三层行政机构—
O_DIRECT / fsync系统调用语义绕缓存 / 强制落盘redo log 的持久化保证
io_uring / SPDK高性能 IO批量提交 / 干脆绕开内核连接池 / 独占直连
RAID单机多盘组织多块盘拼冗余和性能—
RDMA网络远端内存直接读写,绕内核协议栈高性能主从复制通道
NVMe-oF网络协议把 NVMe 方言直接跑网上DB 直连分布式存储
fio工具磁盘压测标准工具sysbench / pgbench
dist缩写网友说 distributed(分布式)的缩写,不是术语—

1.3 带宽数字基准(建议背下来)

看懂一切性能宣称的锚点。没有这些数,你无法判断一个数字是”猛”还是”吹”:

对象数量级备注
HDD 顺序读~0.2 GB/s机械盘天花板
SATA 接口~0.6 GB/s接口上限,再快的 SSD 插上也卡在这
PCIe 3.0 x4 NVMe~3.5 GB/s老一代消费级
PCIe 4.0 x4 NVMe~7 GB/s当前主流,企业级可到 7~14
PCIe 5.0 x4 NVMe~14 GB/s新一代,3FS 类系统用
单机内存带宽200~400 GB/s比盘快一个数量级以上
100Gb 网卡~12.5 GB/s分布式系统的真实瓶颈常在网不在盘
8 盘 PCIe 4.0 服务器理论 ~56,实测 ~40 GB/s”单机 40GB/s”的典型出处

一个心算技巧:PCIe 每代每 lane 约 1GB/s 上下(3.0≈0.98,4.0≈1.97,5.0≈3.9),NVMe 盘占 x4,所以单盘上限就是 4×这个数。看到”单机 40GB/s”,心算一下:40 ÷ 7 ≈ 6 张 PCIe 4.0 盘聚合——嗯,合理,但必是多盘聚合,绝无可能是单盘。

1.4 方法论:看任何性能数字先问三问

这是全书最重要的一节。以后你看到任何吞吐宣称(简历、论文、社区帖、面试官的暗示),都自动过一遍:

  1. 测在哪一层?
    介质 spec(纸面)→ fio 裸盘 → 文件系统之上 → 应用层(含序列化、锁、内存拷贝)。越往右数字越缩水。
  2. 单点还是聚合?
    单盘 / 单机多盘 / 集群 N 台总和(dist)。单盘数字最硬,集群数字随规模想多大有多大,含金量最低。
  3. 什么负载?
    顺序大块读(最漂亮的数字)/ 随机小块(最接近真实业务)/ 混合读写(最惨)。宣称数字几乎永远是顺序大块读。

评论区那句”40GB/s 是盘的带宽还是 dist 的带宽”,其实就是第 1、2 问的口语版。而”真的假的”的答案是:如果是”单机 6~8 张 PCIe 4.0 盘 fio 顺序大块读聚合”,那它不但真实,甚至平平无奇;但如果是”单张盘”或者”应用到应用”,那就是吹的。数字不会说谎,但口径会说谎。

第二章 HDD:理解机械盘,才能理解数据库的”历史包袱”

虽然 SSD 已经是绝对主流,但数据库世界的大量设计(B+ 树、顺序 WAL、预读、聚簇索引)都是被机械盘”逼”出来的。不学 HDD,你就只知其然。

2.1 机械盘是怎么工作的

一块 HDD 由若干盘片(platter)组成,每个盘片正反两面都有磁介质,由主轴电机带动恒速旋转(7200 RPM = 每分钟 7200 转)。数据磁化在盘片上一个个同心圆的磁道(track)里。每面盘片上方悬着一个磁头(head),读写时要先:

  1. 寻道:磁头臂像黑胶唱片机那样物理摆动,把磁头移动到目标磁道上方——机械动作,典型耗时 410ms;
  2. 旋转延迟:等盘片转过来,让目标数据转到磁头下方——平均是半圈的时间,7200 RPM 下约 ~4ms;
  3. 传输:数据顺序流过磁头下方,速度约 0.1~0.25 GB/s。

一次随机读 ≈ 寻道 + 旋转延迟 + 传输 ≈ 8~12ms。这就是 HDD 随机 IO 慢的物理根源:不是电在慢,是机械臂在慢、盘片在等。

2.2 顺序 vs 随机:天壤之别的物理原因

差距是上千倍。这是存储世界最重要的一组对比,也是后面一切故事的主角:

2.3 HDD 时代的两个遗产概念

IOPS 与带宽的关系:HDD 上两者被机械绑死——100 IOPS × 4KB = 每秒才 0.4MB 随机;顺序时却 200MB/s。所以老 DBA 优化数据库,本质上是在减少寻道次数(这是理解老式数据库调优的钥匙)。SSD 时代这个耦合被解开(第 3 章),所以很多老经验失效。

SMR(叠瓦式记录):为了让盘片密度更高,磁道像屋顶瓦片一样叠着写,导致改写一段数据可能要连着重写后面一串——随机写性能雪崩。它是 HDD 里的”写放大”亲戚,也提醒你:买盘(尤其冷数据归档盘)要看清是不是 SMR。

2.4 HDD 今天的定位

单盘 0.2 GB/s、100 IOPS 的指标在 SSD 面前毫无还手之力,但 HDD 每字节成本仍比 SSD 低数倍,所以它退守到冷数据、归档、对象存储底层:照片视频的冷层、备份、训练数据集的仓库。云厂商的数据中心里硬盘依然是按 EB 计的——你刷小红书时热数据在 SSD/内存,三年前的笔记在 HDD 集群里躺着。理解这个分层,就看懂了现代数据系统的”冷热分层”为什么长这样(数据库里对应 buffer pool → 磁盘 → 归档表空间)。

第三章 SSD 与 NAND 闪存

3.1 NAND 闪存的三个物理约束

NAND 闪存的存储单元是”浮栅晶体管”:往栅极里注入/抽出电荷代表 1 bit。这个朴素的机制带来三条铁律:

  1. 读的最小单位是 page(典型 4~16KB),读非常快(微秒级)。
  2. 写的最小单位也是 page,而且一个 page 只能”从空写到满”,不能原地覆盖。想改一个字节?只能把整页读出来、改好、写到另一个空页去。
  3. 擦除的最小单位是 block(几十上百个 page 组成,MB 级),擦除很慢,而且擦一次少一次:每个 block 的可擦写次数(P/E cycles)有限——SLC ~10 万次、MLC ~3000 次、TLC ~1500 次、QLC ~几百到 1000 次。

第三条推出了消费级市场的规律:容量越大的单元(一个栅里塞 3、4 bit),越便宜但越慢、越短命。企业级 TLC/QLC 靠更好的主控和固件弥补。

NAND 闪存是 SSD 内部真正保存数据的介质

┌──────────────────────── SSD ────────────────────────┐
│                                                    │
│  SSD 控制器 ── DRAM / SRAM 缓存                    │
│      │                                             │
│      ├── NAND 通道 0 ── NAND 芯片                  │
│      ├── NAND 通道 1 ── NAND 芯片                  │
│      ├── NAND 通道 2 ── NAND 芯片                  │
│      └── NAND 通道 3 ── NAND 芯片                  │
│                                                    │
└────────────────────────────────────────────────────┘

3.2 NAND 闪存的存储方式

TLC 和 SLC 都是 NAND 闪存的存储方式。区别不在于 Cell 数量,而在于:每个 Cell 存多少 bit,以及需要区分多少种电压状态。

NAND 的基本存储单元叫 Cell。Cell 通过保存不同数量的电子,形成不同的阈值电压。读取时,控制器并不是直接观察“0 或 1”,而是测量 Cell 的阈值电压,然后判断它属于哪个电压区间。

SLC 和 TLC 的根本区别,就是把电压范围切成几档。

3.3 FTL(Flash Translation Layer)

操作系统以为自己在对一块”可随机覆盖读写的地址空间”(LBA,逻辑块地址)做 IO,但 NAND 物理上根本不支持覆盖写。中间靠盘里的固件 FTL(Flash Translation Layer) 打圆场,它维护:

由此产生最重要的指标——写放大系数(WAF)= 实际写入 NAND 的数据量 / 应用写入的数据量。GC 搬移有效页、映射更新都是额外写入,典型 1.5~5 倍。它和 LSM 的写放大互为镜像,数据库的同学看一眼就懂。

3.4 为什么消费级 SSD”跑一会儿就跳水”

消费级盘拿一部分 TLC 容量临时伪装成 SLC 当写入缓存(就叫 SLC cache):来者不拒、先飞快写进这段缓存,后台再慢慢”降格”成 TLC 落盘。缓存写满之后,写入必须走真实的 TLC 慢路径,速度直接腰斩再腰斩——这就是 fio 长时间压测后”速度跳水”的机制。所以任何 SSD 性能数字都必须说”稳态(steady state)“还是”峰值(fresh out of box)“,这是存储版的”新机器跑分”。企业盘靠两个手段避免跳水:

3.5 SSD 与 HDD 的关键差异总结

维度HDDSSD对数据库的影响
随机 vs 顺序差 100~1000 倍差距缩小到几倍”必须避免随机 IO”的教条松动
延迟~10ms~100usRT 里”落盘”的代价从毫秒变微秒
稳态性能恒定受 GC/SLC cache 影响波动压测要”先填一遍盘再看稳态”
写入代价均匀有寿命、有 WAF写密集场景要考虑寿命预算
原地更新可以不行(FTL 模拟)更新重负载下 FTL/LSM 的 GC 压力都变大

最后留一个心智模型收尾:SSD ≈ 一只自带 GC 和寿命管理的迷你 LSM。读快、顺序写快、随机写靠 FTL 兜底但有放大、稳态 ≠ 峰值。记住这个模型,后面看 KVCache store 的设计(第 12 章)会处处似曾相识。

第四章 总线与协议:PCIe、SATA、NVMe 到底谁是谁

一句话先行:PCIe 是路,NVMe 是跑在路上的方言,SSD 是住在路尽头的仓库。路的限速再高,方言低效也白搭;方言再高效,路是单车道也快不了。

4.1 PCIe:一条点对点的高速公路

PCIe 是 CPU 与外围设备之间的主通道,不只是盘——网卡、GPU、加速卡都插在上面。它的几个关键概念:

Lane( lane = 车道):PCIe 是串行点对点链路,一条 lane 包含一对发送一对接收线。设备按占用的 lane 数标注:x1(网卡常见)、x4(NVMe 盘)、x8/x16(GPU、100G 网卡)。

每代带宽(单向、每 lane,十进制近似):

代数每 lane 带宽x4 总带宽NVMe 盘实测顺序读
PCIe 3.0 (2010)~1 GB/s~4 GB/s~3.5 GB/s
PCIe 4.0 (2017)~2 GB/s~8 GB/s~7 GB/s
PCIe 5.0 (2019)~4 GB/s~16 GB/s~14 GB/s

拓扑与预算:CPU 的 PCIe lane 总数有限(典型服务器 CPU 80~128 条),不够用时设备通过 PCIe Switch(类似交换机)分享上行带宽。8 张 x4 盘全直连 CPU 各占 x4 没问题;但 24 张盘就要过 switch,多张盘共享 x16 上行——盘插得多不代表带宽线性涨,这是”单机聚合带宽”测算时必须看的拓扑。另外交换芯片本身有转发上限,也是常见的隐形瓶颈。

降速(degradation):盘与 CPU 的链路会协商到双方都支持的最高代数,散热不好或线路质量差会降到低代——宣称 PCIe 5.0 的盘实际跑在 3.0 上,带宽直接只剩 1/4。排查时用 nvme 命令或 lspci 看 link status(LnkSta)。

4.2 SATA 与 AHCI:上一个时代的遗产

SATA 是 2003 年定下的盘接口,理论 0.6 GB/s,物理上一条 SATA 口只连一个设备。它绑定的协议 AHCI 有个致命设计:全系统只有一条队列,深度 32——因为它是为机械盘设计的,机械盘一次也处理不了 32 个请求(寻道都忙不过来)。

闪存时代这成了枷锁:NAND 内部是并行的(多个 die 同时读),一个 4KB 请求几十微秒就能完成,单队列深 32 意味着主机一次最多压给它 32×4KB,盘喂不饱,闪存的并行能力全浪费。所以 SATA SSD 最多只能跑出 0.55 GB/s 左右——被接口和协议双重卡死。这就是”再快的 SSD 插 SATA 口也只能 0.5 GB/s”的原因。SAS 是服务器版 SATA,双口冗余,现在也主要剩历史存量。

4.3 NVMe:为闪存量身定制的协议

NVMe(2008 立项、2011 发布 1.0)干的事一句话概括:把”单队列深 32”改成 64K 个队列、每队列深 64K,直接挂在 PCIe 上跑。

为什么这个改动是质变:

  1. 多队列:每个 CPU 核可以有自己的提交队列(SQ)和完成队列(CQ),IO 提交不再需要跨核抢锁
  2. 深队列:上万深度可以把 SSD 内部的并行 die、通道(channel)全部喂满,闪存吞吐是”并行度 × 单通道速度”的乘积,队列浅就打不满乘法;
  3. 中断聚合、MSI-X:完成事件可以攒批通知 CPU,减少中断风暴

由此,NVMe 盘的速度 = 介质速度、PCIe 带宽、队列深度三者取最小。日常说”NVMe SSD”实际是”NVMe 协议 + PCIe 总线 + NAND 介质”的打包缩写,这也是社区词汇混用的根源。

4.4 NVMe over Fabrics:把方言搬到网络上

NVMe-oF = 把 NVMe 协议报文封装后跑在网络(RDMA 或 TCP)上, initiator(发起端)像访问本地盘一样访问远端的 NVMe 盘。意义:远端存储跳过”远端文件系统 + 块设备”的多层翻译,直接说方言,延迟更低、路径更短。分布式存储的某些产品(如NVMe-oF target 模式的 Ceph、各类”盘阵直通”方案)就靠它。

4.5 带宽预算:一台服务器的账怎么算

综合本章,给你一个单机带宽的”记账模板”,以后看到单机吞吐宣称可以自己审计:

单机聚合带宽 ≤ min(
    盘数 × 单盘带宽,             # 介质与协议上限
    PCIe 上行总带宽,              # 拓扑限制(switch 或 CPU lanes)
    CPU 处理 IO 的能力,           # 中断、软中断、拷贝
    内存带宽的余量
)

例:8 张 PCIe 4.0 x4 盘直连 CPU → 盘侧 56 GB/s,PCIe 够,理论 56;实际受 CPU 中断/调度、FTL 稳态等影响,实测 40 GB/s 左右——这就是社区里”单机 40GB/s”的合理出处:8 盘级服务器的顺序读聚合,一个不多见的、但毫不魔幻的数字。

第五章 Linux IO 栈:一条 write() 从应用到盘的完整路径

这章是全书”软件层”的主干。理解了这条路径,你才能理解 O_DIRECT、fsync、页缓存、io_uring 这些数据库绕不开的词,也才能理解”fio 数字和应用层数字差在哪”。

5.1 五层行政机构

一条 write() 从应用到 NAND,要穿过五层:

write() 系统调用(用户态 → 内核态,一次上下文切换)
  ↓
① VFS(虚拟文件系统):统一接口层,认出"这是个 ext4 文件"
  ↓
② Page Cache(页缓存):内核的"buffer pool",4KB 一页
  ↓
③ 文件系统(ext4/XFS):维护 文件偏移 → 磁盘块 的映射(inode/extent)
  ↓
④ Block Layer(块层):把"写这些块"变成请求 bio,排队、合并、调度
  ↓
⑤ 驱动(NVMe):把请求塞进 SQ 环,盘干完活回 CQ 环,中断通知
  ↓
盘内 FTL → NAND

数据库人读这张图最该产生的两个反应:

  1. 页缓存 = 一个我不受控的 buffer pool。它对读是好事(免费缓存),对写却意味着”write() 返回 ≠ 落盘”——数据停在内存里,内核认为”待会儿再刷”。这正是数据库 redo log 必须 fsync 的原因:数据库要自己做持久化承诺,不能依赖内核的”待会儿”。
  2. 我写一条数据竟然要过五个衙门。每个衙门都有拷贝、加锁、排队。所谓高性能存储系统(SPDK、io_uring),本质都是在裁衙门。

5.2 页缓存的读写路径

读:write 不涉及。读时先查页缓存,命中直接返回(内存速度),未命中才发 IO 到块层,读回来放页缓存再拷给应用。

写(默认 write-back 模式):write() 把数据写进页缓存,标记脏页(dirty),立即返回成功。之后内核的 flusher 线程按”脏页比例/年龄”阈值把脏页成批刷盘。两个后果:

5.3 持久化的语义谱系

语义行为代价类比
write()进页缓存就返回快,不持久”提交到事务缓冲”
fsync(fd)把该文件所有脏页和相关元数据刷到稳定存储才返回一次真实落盘”提交日志并确认”
fdatasync(fd)只刷数据,跳过不必要的元数据(如文件大小不变时的 mtime)略便宜”只提交事务本身”
O_SYNC 写每次 write 自带落盘每条都付一次 fsync 代价”每语句一提交”
O_DIRECT绕开页缓存,直通块层省一份内存拷贝,但绕缓存不等于持久!“跳过 buffer pool 直接裸写”

最经典的误区标在最后一行:O_DIRECT 只解决”绕缓存”,不解决”何时持久”。InnoDB 用 O_DIRECT 之后依然要靠 redo log fsync 保证持久性。所以 InnoDB 的组合拳是:O_DIRECT(避免数据页在页缓存 double 缓存浪费内存、避免内核回写时机不可控)+ 每次 commit fsync redo log(拿持久化承诺)。

page cache 的双重缓存问题:如果数据库自己管 buffer pool(InnoDB 就是),内核页缓存又存一份,同一页在内存里躺两份,浪费一半内存且增加一次拷贝。这就是数据库偏好 O_DIRECT 的核心动机。

mmap:把文件映射进进程地址空间,读文件 = 读内存,缺页时内核自动拉数据。省拷贝,但缺页中断不可控、写入持久化语义同样要 msync,生产数据库基本不用它管理热数据页,反而在分析场景(如某些列存、索引只读段)常见。

5.4 从 read() 到 io_uring:syscall 贵在哪、怎么省

传统同步 IO 一次 read = 一次上下文切换(用户态→内核态)+ 一次内存拷贝(页缓存→用户缓冲区)。单次才几微秒,看起来便宜,但在百万 IOPS 时 CPU 全在切换和拷贝上,盘反而空闲。

5.5 观测工具箱

工具看什么
iostat -x 1每盘 r/s、w/s、rMB/s、wMB/s、await、util%
pidstat -d按进程 IO,定位”谁在打盘”
blktrace / bpftrace块层延迟分解(排队时间 vs 盘执行时间)
iotop实时按进程 IO 的 top
/proc/meminfo(Dirty/Writeback)脏页存量,判断回写压力
nvme smart-log盘健康、剩余寿命、链路降速

两个高频陷阱(老 DBA 也常踩):

  1. iostat 的 util% 满不等于盘饱和。util% 是”队列非空的时间占比”,HDD 时代近似”忙”,但 NVMe 天生并行,100% util 时吞吐可能还在涨。看吞吐和 await 比 util 更可靠。
  2. await 高不一定是盘的锅。await 是”从进块层到完成”的总耗时,排队太深、CPU 饿死 flusher、上游锁竞争都会抬高它。用 blktrace 拆”排队段 vs 盘执行段”再下结论——这和”慢查询不一定是执行慢,可能在等锁”是同一套排查逻辑。

第六章 文件系统

文件系统(File System, FS)解决的问题:把”一块只能按扇区读写的无结构磁盘”组织成”按路径找文件的世界”。它本质上是一个存放在磁盘上的小型数据库:inode 表是它的”系统表”,目录是它的”索引”。

6.1 文件系统在管什么

一个文件 = inode(元数据)+ 一串数据块。

数据库类比总结:inode 表 = 系统表;目录 = 二级索引;extent 分配器 = 空闲空间管理表。

6.2 崩溃一致性

断电瞬间,正在写的 FS 处于”数据写了一半、元数据写了另一半”的随机状态,重启后可能出现”文件大小变了但数据是旧的”这类怪象。FS 的应对是日志(journal)——先写下”我要改哪些元数据”的意图,再动手改,崩溃后按日志补齐或回滚。这不是巧合,这就是 write-ahead 的同一种思想,只是元数据是它的”事务”:

数据库人最重要的结论:FS 日志只保证”文件系统结构不坏”,不保证”我的文件内容完整”。应用一个事务写到一半断电,FS 重启后完好无损,但你的文件只有半条事务。所以数据库必须在 FS 之上再造一层(redo log + doublewrite)。

6.3 主流文件系统选型速览

FS特点谁在用
ext4最主流,稳,extent + journal,中庸默认选择
XFS大文件、高并发分配强,元数据并行佳RHEL 默认;数据库/大文件
ZFS/btrfsCoW、快照、校验和、子卷NAS(TrueNAS)、容器存储
tmpfs纯内存 FS/dev/shm,临时数据

数据库/存储系统的常见姿势:要么选 XFS + 调整挂载参数;要么干脆绕过 FS(如直接管理裸设备的 KV 存储、SPDK blobstore),裁掉这层衙门。

6.4 RAID:多盘的组织术(放本章收尾讲最顺)

单机数据可靠的另一半是盘组织问题。盘会坏,坏了一台机器的数据就没了;盘也慢,一盘 7GB/s 要打 40GB/s 就要聚合。RAID(独立磁盘冗余阵列) = 多盘按某种策略拼起来同时解决”坏不坏”和”快不快”:

级别组织方式容错空间利用率写惩罚
RAID 0纯条带(数据轮流分给各盘)0 坏100%1×
RAID 1镜像(两盘互拷)1 块50%写两份
RAID 5条带+分布式校验1 块(n-1)/n写要同时改数据+校验(读改写,约 4 次 IO)
RAID 6双校验2 块(n-2)/n约 6 次 IO
RAID 10先镜像后条带每对坏 150%2×,写快读快,DB 时代经典

RAID 5/6 的写惩罚:一次小块写在”老值新值都要读出来才能算新校验”时变成读旧数据 + 读旧校验 + 写新数据 + 写新校验 = 4 次 IO。所以随机写密集的数据库从来不上 RAID5,上 RAID10。

注意”分布式时代的观念迁移”:单机 RAID 卡方案在云和分布式时代在退化——大型分布式存储干脆 JBOD(不组 RAID,盘直挂),把冗余上移到集群层(三副本/EC,第 10 章),把”修一块盘”的事交给上层软件。单机 RAID 仍活在传统 DB 服务器和存储阵列里。

第七章 性能测量:IOPS、吞吐、延迟与 fio 实操

这章是前六章的”变现”——学完概念后,你要能亲手打出数字、并能审计别人的数字。也是回看”40GB/s”争论时的收官武器。

7.1 三大指标与它们的关系

指标含义数据库类比
IOPS每秒 IO 次数QPS
带宽(Bandwidth/吞吐)每秒字节数 GB/s每秒处理的数据量
延迟(Latency)单次 IO 耗时,看 P50/P99/P999RT 分布

换算公式:带宽 = IOPS × 块大小(bs)。这一个公式立刻解释一个反直觉现象:同样打满 7 GB/s,用 1MB 块只需 7000 IOPS,用 4KB 块需要 175 万 IOPS——顺序大块测的其实是带宽,随机小块测的其实是 IOPS。厂商宣传的”700 万 IOPS”和”14 GB/s”是同一张盘在不同负载下的两副面孔。

第四个常被忽略的维度:队列深度(QD)。单线程发一个请求、等回来、再发下一个,哪怕盘再快也只有”1/延迟”的吞吐。要打满盘必须多请求并发灌进去(fio 用 numjobs/iodepth,NVMe 的 64K 队列就是为此准备)。存储圈的黑话:深度大吞吐高但延迟涨(排队),深度小延迟低但打不满——这就是吞吐-延迟曲线永远在权衡的形状,和数据库并发-RT 的权衡完全同构。

7.2 fio:磁盘界的 sysbench

fio(Flexible IO Tester)是事实标准。参数→概念映射:

fio 参数作用对应概念
—rw=read/randread/write/randwrite顺序/随机读写负载模型
—bs=1M / 4k块大小测带宽还是测 IOPS
—direct=1绕页缓存必加,否则测的是内存
—numjobs=44 个线程并行队列深度
—iodepth=32每线程在途请求数排队长度
—runtime=60 —time_based固定时长跑取稳态样本
—group_reporting多线程汇总成一个数报口径

四个基础实验:

# ① 顺序大块读:测单机带宽上限(对应"40GB/s"类宣称的口径)
fio --name=seqread --rw=read --bs=1M --direct=1 \
    --numjobs=4 --iodepth=32 --size=4G --runtime=60 --time_based \
    --group_reporting --filename=/path/to/testfile

# ② 随机 4K 读:测 IOPS(OLTP 的真实压力形状)
fio --name=randread --rw=randread --bs=4k --direct=1 \
    --numjobs=4 --iodepth=32 --size=4G --runtime=60 --time_based \
    --group_reporting --filename=/path/to/testfile

# ③ 随机 4K 写:测稳态(看消费级盘 SLC cache 跳水)
fio --name=randwrite --rw=randwrite --bs=4k --direct=1 \
    --numjobs=4 --iodepth=32 --size=4G --runtime=600 --time_based \
    --group_reporting --filename=/path/to/testfile

# ④ 混合 7:3 读写:最接近业务
fio --name=mixed --rw=randrw --rwmixread=70 --bs=8k --direct=1 \
    --numjobs=4 --iodepth=32 --size=4G --runtime=60 --time_based \
    --group_reporting --filename=/path/to/testfile

读数要点:看 fio 输出的 read: IOPS=xxx, BW=xxx 之外,一定要看 lat (usec) 的 avg 和 99.00th%——IOPS 高但 P99 抖上天的盘,做数据库主存是灾难。

7.3 稳态:测试前先把盘”打服”

第 3 章说过 SSD 有 SLC cache 和 GC,全新盘的首次测试会看到”巅峰速度”,毫无参考意义。标准流程是先填一遍盘再测(precondition):

# 预热:顺序写填满整盘 1~2 遍,逼出 FTL 真实工作状态
fio --name=prep --rw=write --bs=1M --direct=1 --filename=/dev/nvmeXn1 \
    --size=100% --loops=2

企业级测试规范(SNIA PTS)要求写两遍以上再取数。面试里说出”我看的是稳态数字,不是开箱数字”这句话,直接把你和背参数的人区分开。

7.4 报数合规:三问的落地模板

结合第 1 章的三问,任何数字发布前的自检清单:

  1. 测试点:direct=1 了吗?测的是裸盘还是文件系统上?应用层又另测了吗?
  2. 聚合口径:几块盘、几台机、numjobs/iodepth 多少?单盘 or 单机 or 集群(dist)?
  3. 负载匹配:rw 类型、bs 大小、读写比例、是否稳态(先填盘了吗)、跑了多久、P99 延迟多少?

标准报数句式(背下来直接用):

“40 GB/s 是单台服务器 8 张 PCIe 4.0 NVMe 企业盘、fio 顺序读 bs=1M、direct=1、numjobs=8、预热后稳态的单机聚合值;P99 延迟 xx us。应用层(KVCache 真实块大小 64KB 前缀命中负载)实测 xx GB/s,瓶颈在 [网络/序列化/CPU]。”

对照这个模板,你就能秒判社区里任何带宽宣称的成色:缺”单盘还是聚合”说明是外行;缺”稳态”说明没做过正经测试;缺”负载形状”说明没想过真实场景。40GB/s 这个数本身不惊人——惊人的一直是口径的清晰度。

第八章 网络存储通道:RDMA 与”瓶颈常在网”

单机故事讲完了。要把 N 台机器的盘拼成一个集群(这就是 KVCache store / 分布式数据库的形态),机器之间搬运数据的通道就成了新主角。这章讲清楚:为什么”分布式带宽”的第一瓶颈往往不在盘、在网。

8.1 传统 TCP 的代价

普通网络(TCP + 以太网)传一份数据的路径:

应用缓冲区 → 拷贝到内核 socket 缓冲 → 协议栈封装(TCP/IP)→ 网卡
     —————— 物理线路 ——————
网卡 → 协议栈解封装 → 拷贝到应用缓冲区

两次内存拷贝 + 两次协议栈处理 + 多次中断。单看一笔没问题,但在 40GB/s 量级时,CPU 光做协议栈和拷贝就烧满了,传输通道成了整条链路最窄处。类比:盘是 8 车道高速公路,TCP 是每个包都要过两道安检。

8.2 RDMA:远端内存直接读写

RDMA(Remote Direct Memory Access):网卡自带智能,应用预先注册一块内存区,之后一方可以直接读写对方网卡登记的内存区域,全程不经过任何一方的操作系统内核——没有协议栈、没有内核拷贝,延迟从几十微秒降到 1~5 微秒,CPU 几乎零参与。

类比:TCP 是”寄快递,双方前台签收”;RDMA 是”你有我家钥匙,直接从我桌上拿东西”——所以我必须先”授权登记你家地址”(内存注册),你也只能拿登记过的东西(保护边界)。

RDMA 的三种操作(高性能系统常提):

物理载体两种:InfiniBand(专用线,延迟最低、贵)和 RoCE(RDMA over Converged Ethernet,普通以太网跑 RDMA,数据中心主流,便宜但需要无损网络配置 PFC/ECN——否则”以太网丢一个包,RDMA 重传整条消息”,性能雪崩)。

带宽预算:100Gb/s 网卡 = 12.5 GB/s,200Gb = 25 GB/s。单台 8 盘 40GB/s 的机器,如果只有一张 100Gb 网卡,对外的可用带宽被网络掐到 12.5GB/s。这就是”单机 40GB/s”和”集群对外服务带宽”之间巨坑的来源——盘打得再快,网就那么宽。这就是为什么那些宣称聚合 40GB/s+ 的系统要插 2~4 张 200G 网卡。

8.3 NVMe-oF:把”盘的方言”搬到网上

第 4 章提过:NVMe-oF 把 NVMe 协议报文通过 RDMA(或 TCP)转发,让 initiator 直接说”盘的语言”访问远端盘,省掉远端文件系统一层。意义再强调一次:每省一层衙门,延迟少一截、CPU 省一块。分布式存储里”计算节点直读存储节点裸盘”的方案多走这条路。

8.4 零拷贝全家福(本机侧的配合)

除了 RDMA,本机内减少拷贝的常见武器,一并认清:

8.5 本章结论:分布式带宽的记账公式

把第 4 章”单机带宽记账”扩展到集群:

集群有效带宽 ≈ min(
   N × 单机盘带宽,            # 介质总供给
   N × 单机网卡带宽,          # ← 常是短板(12.5/25 GB/s 每机)
   交换机集群总容量,          # 交换机 radix × 端口带宽
   副本/EC 放大系数,          # 3 副本把客户端可见带宽除以 3(下一章)
   CPU 序列化/校验开销
)

所以看到”集群聚合 xx GB/s”的宣称,先做除法:单机对外带宽 × 机器数 ÷ 副本数,对不上号就有鬼。这个心算工具足够你审阅 90% 的分布式存储宣传。

第九章 分布式存储基础:把 N 台机器的盘拼成一个系统

单机的盘再多也有上限,生产存储必须是分布式的:几十上百台机器、每台几块盘,用一个软件层拼成”一台逻辑上的大存储”。这章讲清这个软件层的通用骨架——学完后看 HDFS、Ceph、3FS 都是同一些模式的不同排列组合。

9.1 为什么分布式:可用性与扩展性

盘会坏,机器会断电,机房会断网。单机无论怎么 RAID,整机一挂全完。分布式的两个承诺:

9.2 通用骨架:元数据层 + 数据层

几乎所有分布式存储都是”控制面 + 数据面”分离:

分片单位(chunk/block/shard)的典型大小是 MB 级:太小元数据爆炸,太大负载不均衡。类比数据库的分区表粒度选择。

9.3 冗余:三副本 vs 纠删码(EC)

数据放 N 台机器上,怎么防机器坏?

三副本(3x replication):每块数据写三份放三台机器。优点:读可以挑最快的副本,实现简单,修复快(任何一台坏,从另外两台拷)。缺点:空间利用率 33%,写的网络放大 3 倍。数据库集群的多数派、KVCache store 的默认姿势。

纠删码(Erasure Coding, EC):用数学换空间。把数据切成 k 份、算出 m 份校验(记 EC(k+m)),任意丢 m 份都能解方程恢复。例如 EC(8+3):11 份存储 8 份数据,利用率 73%,且坏 3 台不丢。缺点:坏一份要读剩下 8 份来恢复(修复成本高,HDD 集群”修复风暴”事故常客);小写入要读旧分片重算校验(读改写放大,所以 EC 常只用于冷数据/大文件)。类比:RS 纠删码就是 RAID 5/6 的推广版,只是校验分布到全集群而不是单机。

成本敏感的大规模系统(对象存储的冷层)几乎全是 EC,热层和写密集层用三副本——又是冷热分层。

9.4 一致性:副本之间怎么不打架

三副本写下去必然面临”写到第二台成功第三台失败,算不算写成功?“分布式的一致性从这里展开。收敛到你需要掌握的最小集合:

9.5 心跳、故障与再平衡

维持一个”永远有机器坏”的集群的日常机制:

一个隐含的时间问题叫脑裂:两拨机器互相认为对方死了、各自当老大。解法是多数派选举——挂掉的少数派自动失效。这就是为什么 quorum 里 2/3 活着才干活,2 节点挂 1 个后为什么直接不能写。

9.6 三大经典系统对号入座

系统形态关键点
HDFS分布式文件系统NameNode 元数据 + DataNode 数据;三副本;一次写多次读;大数据时代的地基
对象存储(S3/Ceph RGW)键值对象海量小对象、EC 冷层、REST 接口;互联网图片视频的标准答案
Ceph统一存储CRUSH 算法算数据位置(去中心化元数据);块/文件/对象三接口合一
3FS高性能文件系统RDMA + NVMe 直通,为 AI 训练/KVCache 设计(第 12 章主角)

9.7 聚合带宽的诚实算法

把第 8 章的记账公式补全,分布式聚合带宽宣称的审计步骤:

单机贡献 = min(单机盘聚合带宽, 单机网卡带宽)
集群聚合 ≤ N × 单机贡献 ÷ 副本数(或 × EC 写放大)
再减去恢复流量 / 再平衡流量的抢占比

例:100 台机器、每台 8 盘 40GB/s 但网卡 25GB/s、三副本 → 理论聚合 ≈ 100×25÷3 ≈ 830 GB/s。假如宣称”500GB/s”要打问号时,就用这个公式先心算。“dist 带宽”必须报机器数和副本数才有意义——这就是评论区追问”是盘还是 dist”的深层理由。

第十章 复盘:存储如何塑造了数据库

这章把你最熟悉的数据库设计逐个”回炉”到前九章的介质知识里——每一条都解释成”被某层存储特性逼出来的设计”。这是数据库背景学存储的最大红利:你不是新学,是给你已有的知识补上因果链。

10.1 页(page)大小的因果链

为什么 InnoDB 是 16KB、PG 是 8KB?因为 HDD 一次寻道 10ms 极贵,“一次寻道多拿数据”是铁律,所以页要大;但页太大又伤随机更新的写放大(改 1 字节重写整页)和内存占用,所以停在 816KB 的折中。SSD 的 NAND page 是 4~16KB,DB 页继续对齐它避免”一次读拿两页”的浪费。页大小 = 介质最小单位与写放大之间的折中。

10.2 B+ 树:为”减少随机 IO”而生

HDD 随机读 10ms、顺序 0.2GB/s,相差三个数量级。B+ 树的全部设计都服务于”把任意点查压缩到尽量少的随机读”:

10.3 LSM:为”把随机写变成顺序写”而生

B+ 树的代价在写:每次更新都可能改任意树中任意页 = 随机写(HDD 上 100 倍惩罚,SSD 上烧 FTL 寿命)。LSM(RocksDB/LevelDB/HBase/许多 KVCache 元数据层)反过来:

10.4 WAL 与 fsync:持久化的最后一公里

前 5 章的页缓存知识在这里变现。数据库的 ACID 之 D 全靠这一套:

  1. 修改先以逻辑形式顺序追加写进 redo log(顺序 = 快,任何介质上都是);
  2. fsync 之前,“已写”不等于”已持久”(页缓存还在内存里);
  3. commit = 把这次事务的 redo fsync 落盘才向客户端回 ack——这就是 innodb_flush_log_at_trx_commit=1 的语义;调成 2 就是”只写 OS 缓存、每秒才 fsync”,机器断电丢 1 秒事务,内核崩溃不丢——这个经典参数每一档的差别,正是第 5 章页缓存/ fsync 层级的直接应用。
  4. 数据页本身脏了由 buffer pool 慢慢刷(checkpoint),坏了就用 redo 重放——“日志先行,数据页异步收敛”和文件系统的 journal 思想(第 6 章)同宗。

group commit:多个并发事务的 fsync 攒批一次落盘,摊薄每次 100us~ms 级的固定成本——和 io_uring 的批量提交(第 5 章)是同一思想,也是数据库人最快理解高性能 IO 的桥。

10.5 doublewrite:文件系统不懂 SSD 的”半页”问题

InnoDB 要写一个 16KB 页,交给文件系统后底层可能按 4KB 扇区分几次写,断电写一半 = “页撕裂”(16KB 的新页旧页混合体)。redo 重放假设”页要么旧要么新”,撕裂页把恢复逻辑直接击穿。而 ext4 的 journal 只保文件系统自己的元数据(第 6 章结论),不管你文件内容的原子性。解法 doublewrite:先把页连续顺序写到一块”备份区”再写正式位置,恢复时从备份区捡完整页。又是一次介质/软件层语义缺口逼出来的设计。SSD 时代的消解:有些 FS 提供原子写、NVMe 有 atomic write 特性,PG 16 的Relation Deduplication/新内核 atomic write 特性都在试图淘汰 doublewrite。

10.6 buffer pool 与冷热分层:金字塔的中间层

第 1 章金字塔里”内存比盘快 1000 倍”的那个数量级,就是 buffer pool 的存在理由。而它的一切机制都是分布式存储的预演(第 9 章):

10.7 一张因果总表

数据库设计被哪层存储特性逼出来的
16KB 页HDD 寻道贵 + NAND page 对齐
B+ 树矮胖随机 IO 一次 10ms(HDD)/100us(SSD)
顺序 WAL任何介质顺序写远快于随机写
fsync 语义 / trx_commit 参数页缓存 write-back 不等于持久
O_DIRECT页缓存与 buffer pool 双重缓存
doublewrite扇区级非原子写 + redo 假设页原子
LSM / compaction追加写介质友好 + FTL 同款约束
group commitfsync 固定成本高,批摊
冷热分层 / 归档表空间介质价格-性能差几个数量级
分布式架构的多数派/副本第 9 章:机器必坏,冗余必做

学到这,你已经完成了”存储知识 → 数据库知识”的双向翻译。最后一章把它推进到 AI 时代的新场景。


第十一章 KVCache Store 与 AI 时代存储

这是全书的终点。LLM 推理把一种全新的负载抛给了存储系统,这个负载的每个特点你都能用前十章的知识点解释。

11.1 背景:PD 分离与 KV Cache

LLM 推理分两阶段:

把两阶段拆到不同集群(PD 分离,disaggregated prefill-decode)是当前主流架构,好处是两边各自扩容、互不抢资源。而 KV Cache 有个金子般的性质:相同前缀的 prompt,KV 可以完全复用(系统 prompt、文档开头、多轮对话的历史——都是重复前缀)。

于是产生了 KVCache Store:prefill 算好的 KV 落到一个分布式存储集群,decode 端按前缀命中后拉走直接续算,省掉重复 prefill 的算力。它是”算力太贵,用存储换”的产物——和你熟悉的一切缓存系统共享同一条经济学。

11.2 架构四层(对照第 9 章骨架)

元数据层:前缀 → KV block 位置映射(内存哈希/前缀树 + Raft 持久化)
数据层:  KV block 分布式 NVMe 集群 + RDMA 直传(io_uring/SPDK 本地 IO)
策略层:  驻留/淘汰(LRU+热度)、冷热分层(显存→内存→NVMe 三级)
接口层:  store/load API,对接 vLLM/SGLang 的 KV 管理器

和第 9 章”元数据 + 数据 + 副本 + 淘汰”完全同构——KVCache store 就是一个专门为”KV block”优化的分布式 KV 数据库,其中元数据层本身就是个小型 LSM/哈希引擎。

11.3 为什么 KVCache 负载”特殊”(用前十章逐条解释)

这就是 fio 数字会高估它的原因,每条都是旧知识:

  1. 块是”中等大小”:一个 KV block 典型 16512KB(由 16128 个 token 的 KV 组成),不是 fio 的 1MB 大块也不是 4KB 小块。第 7 章公式”带宽 = IOPS×bs”——中等块同时考验 IOPS 和带宽,是两头都沾的难度。小块还吃不满顺序带宽(第 2 章顺序优势吃不到)。
  2. 访问模式是前缀匹配:按 prefix hash/前缀树找命中区间,读是”跳跃读”——命中区间不同 token 数、不同层,地址跳跃,非 fio 的均匀随机也非纯顺序。
  3. 网络是命门:decode 端拉 KV 走 RDMA,100G 网卡 12.5GB/s(第 8 章)掐着脖子。就算 store 端盘能吐 40GB/s,出网就 12.5。这就是为什么这类系统都堆 200G/400G 网卡。
  4. 热点前缀 skew:热门系统 prompt 全网复用 → 少数前缀极高命中率 → 局部盘/局部节点热点(第 3 章 FTL 热点、第 9 章 rebalance 的老问题)。
  5. 消费端限速:decode 集群把 KV 装进 GPU HBM 的速度、PCIe 转接(第 4 章 lane 预算)可能比存储还慢——全链路最窄处常常根本不在盘。

11.4 三个代表系统

11.5 学习路线收官

  1. 读:3FS 博客(拆聚合数字到单机单盘)、Mooncake 论文(架构四层原型)、vLLM KV connector 文档(接口层)。
  2. 跑:第 7 章 fio 四个实验,亲手看顺序/随机/稳态的差距。
  3. 串:回看 InnoDB 的 redo/doublewrite,用第 10 章因果表把每个设计讲给自己听一遍。
  4. 谈资:以后遇到任何带宽宣称,三问 + 带宽记账公式 + “稳态”一句,足够你在任何讨论里站住。

全书完。你已经从”NVMe、PCIe 是啥”走到了能审计 KVCache store 性能宣称的位置——这条路没有跳步,每一层都是下一层的地基。