本文是 机械硬盘工作原理 的进阶篇。与基础篇面向「了解概念」不同,本文站在数据库内核开发工程师的立场:你要写的不是”如何读写一个文件”,而是缓冲池、WAL、B-Tree、checkpoint、崩溃恢复——这些模块的正确性都建立在对磁盘行为的精确假设之上。凡是假设错了就会出数据损坏或丢事务的地方,本文重点展开。

零、为什么数据库内核必须懂 HDD

数据库内核的几乎每一个性能与正确性决策,最终都要落到「这一次磁盘 IO 到底发生了什么」:

  • 正确性fsync() 之后事务是否真的不会丢?断电瞬间正在写的 8KB 页面会不会半截落盘(torn page)?
  • 性能:为什么 B-Tree 的随机读比顺序扫描慢几百倍?为什么 WAL 必须是顺序写?
  • 可靠性:消费级”叠瓦盘”做数据库盘为什么会周期性卡死?RAID 重建时硬盘为何被踢出?

把这些问题答错,上层再精巧的算法都是空中楼阁。下面逐层拆解。

一、物理构成:随机访问为什么贵

一块 HDD 的核心部件:

┌─────────────────────────────────────────────┐
│  主轴电机 (Spindle)  恒速旋转 7200/10k/15k RPM │
│   ┌───────────────────────────┐             │
│   │ 盘片 Platter ×N (双面涂磁) │ ← 同心磁道   │
│   └───────────────────────────┘             │
│   ┌───────────────────────────┐             │
│   │  actuator arm 执臂          │             │
│   │    └ 磁头 Head (GMR 传感器) │ ← 半径摆动   │
│   │      音圈马达 VCM 驱动       │             │
│   └───────────────────────────┘             │
└─────────────────────────────────────────────┘
  • 磁道 (Track):盘片上同心圆;柱面 (Cylinder):各盘片同一半径的磁道集合(切换磁头比移动臂快)。
  • 扇区 (Sector):磁道被切分的弧段,是最小读写单元。
  • 磁头是巨磁阻 (GMR) 传感器,非接触式悬浮在盘片上方约几纳米,靠感应介质剩磁方向读出模拟电压。

随机访问昂贵,根本原因是机械运动:移动磁头(寻道)+ 转动盘片(旋转等待)都要毫秒级,而数据传输本身不到 0.1ms。这是后面所有优化(顺序化、预读、缓冲池)的出发点。

HDD 俯视解剖动画:盘片旋转 + 执臂摆动寻道
图 1:HDD 俯视解剖(动画)。盘片由主轴电机恒速旋转,执臂由音圈马达 (VCM) 驱动沿半径摆动完成寻道;这正是"机械访问昂贵"的物理来源。

二、寻址模型:LBA、CHS 与扇区尺寸(对齐是 DB 的地基)

2.1 LBA → 物理位置的翻译

操作系统和数据库只看到连续编号的 逻辑块地址 (LBA)。控制器把它翻译成物理的 柱面/磁头/扇区 (CHS) 再驱动硬件:

逻辑世界                       物理世界
┌──────────┐                ┌──────────────────────┐
│ LBA 2048 │  固件翻译 →     │ 柱面 k / 磁头 h / 扇区 s │
└──────────┘                └──────────────────────┘

现代硬盘用 区位记录 (Zone Bit Recording):外圈磁道更长、扇区更多,所以「LBA→物理位置」由固件内部的区映射表完成。数据库要知道的关键事实是:相邻 LBA 在物理上基本相邻——这正是顺序 IO 快、随机 IO 慢的根本原因。

2.2 扇区尺寸:512n / 512e / 4Kn(DB 内核必懂)

这是最容易被忽视、却最影响正确性与性能的底层细节。

类型逻辑扇区物理扇区说明
512n512B512B传统盘,无翻译开销
512e512B4KiB仿真模式,最普遍
4Kn4KiB4KiB原生 4K,需 OS/驱动支持
  • 512e 的 RMW 放大:物理扇区是 4KiB,但对外暴露 512B 逻辑块。当你写一个未对齐的 4KiB(跨两个物理扇区)或写小于 4KiB 的块时,盘必须先把整个 4KiB 物理扇区读出 → 修改 → 整体写回(read-modify-write),一次逻辑写变成一次读 + 一次写,延迟与写放大翻倍。
  • 对齐:文件系统/分区/LVM 必须 4KiB 对齐,数据库页(通常 8KiB)也要按 4KiB 边界起址,否则每个页面写都可能触发 RMW。
  • 原子写单元:盘保证「一个物理扇区 (4KiB) 要么完整写入、要么完全没写」。但数据库页面常是 8KiB,跨两个 4KiB 物理扇区。若断电正好发生在写第一个扇区之后、第二个之前——就会出现 torn page(撕裂页):页面一半是新数据一半是旧数据,逻辑上完全不一致。这正是 WAL 里 full-page write(整页写前像) 存在的根本原因——崩溃恢复时用 WAL 里记录的整页镜像覆盖回这个损坏页。

内核开发要诀:永远假设一次”页面写”不是原子的。用 page checksum 检测撕裂/静默损坏,用 WAL full-page 在恢复时重建;并要求存储层 4KiB 对齐,避免 512e 的 RMW。

三、一次随机读:端到端全链路

下图是一次随机读取从发令到返回的完整路径(以 7200 RPM 为例):

flowchart TD
    subgraph H0["① 主机 ↔ 控制器"]
        A["主机: READ LBA n, 长度"]
        B["控制器: LBA → CHS(柱面/磁头/扇区)"]
    end
    subgraph M0["② 机械运动 — 随机读的主要代价 (毫秒级)"]
        C["音圈马达寻道: 磁头移到目标磁道<br/>Seek 3–15 ms"]
        D["伺服定位: 锁定磁道中心 Track Following"]
        E["旋转等待: 目标扇区转到磁头下<br/>Rotational Latency ≈ 4.2 ms @7200RPM"]
    end
    subgraph S0["③ 读通道信号处理 (微秒级)"]
        F["磁头感应磁通变化 → 模拟电压"]
        G["预放 + PRML 读通道: 模拟转数字比特"]
        H["ECC 解码 + 校验"]
    end
    subgraph R0["④ 回传 (传输 < 0.1 ms)"]
        I["写入驱动缓存, 经 SATA/SAS 回主机"]
        J["数据返回"]
    end
    A --> B --> C --> D --> E --> F --> G --> H --> I --> J

    classDef host fill:#e7f5ff,stroke:#1c7ed6,color:#103a5c;
    classDef mech fill:#fde2e1,stroke:#e03131,color:#7a1416;
    classDef sig fill:#e6fcf5,stroke:#0ca678,color:#0a4f3c;
    classDef back fill:#fff3bf,stroke:#f08c00,color:#7a4b00;
    class A,B host;
    class C,D,E mech;
    class F,G,H sig;
    class I,J back;
寻道与旋转等待动画
图 2:一次随机读的两大机械延迟。左:寻道(磁头在磁道间摆动,3–15ms);右:旋转等待(目标扇区转到磁头下,7200RPM 平均约 4.17ms,绿色闪光表示扇区对齐瞬间)。

下图用时序图展示”发令 → 控制器翻译 → 盘片感应 → 返回”的协作过程:

sequenceDiagram
    participant H as "主机 Host"
    participant C as "控制器 Controller"
    participant P as "盘片 Platter"
    H->>C: READ LBA n, 长度
    C->>C: LBA → 柱面/磁头/扇区
    C->>P: 寻道 + 旋转等待
    P-->>C: 磁通变化 → 模拟电压
    C->>C: PRML 译码 + ECC 校验
    C-->>H: 数据经 SATA/SAS 返回

3.1 寻道 (Seek) —— 最贵的一步

磁头装在 音圈马达 (VCM) 驱动的臂上,通电解洛伦兹力沿半径摆动,是典型闭环控制:

目标位置 ──┐
           │  位置误差信号 PES
  伺服逻辑 ─┴─→ 驱动 VCM ─→ 磁头移动
    ▲              │
    └── 从伺服扇区读回实际位置 (闭环校正)
  • 全程寻道 (full stroke) 约 8-15ms,平均寻道约为全程的 1/3。
  • 寻道功耗大、有机械磨损,是随机访问的主要成本。

3.2 伺服定位与磁道跟踪

盘片嵌入 伺服扇区 (Servo Sector),周期性记录精确位置刻度。磁头读伺服得到两路 burst 信号,比较幅度算出 位置误差信号 (PES),微调 VCM 让磁头始终居中:

[数据][伺服][数据][伺服][数据][伺服]...   ← 每磁道间插入伺服
PES > 0 : 偏右 → 向左微调       PES < 0 : 偏左 → 向右微调
伺服闭环定位动画
图 3:伺服闭环(动画)。上方磁头在 PES 驱动下振荡收敛到磁道中心;下方绿色光点沿"目标→比较→PES→VCM→读回位置"循环流动,构成持续校正环路。

没有伺服,磁头会因振动/热膨胀飘出磁道——硬盘怕震动的根源。

3.3 旋转等待 (Rotational Latency)

7200 RPM → 每秒 120 圈 → 每圈 8.33ms → 平均旋转等待 ≈ 4.17ms
15000 RPM → 每圈 4ms    → 平均旋转等待 ≈ 2ms

3.4 读出:从磁场到比特(PRML)

磁头输出微弱模拟电压,经预放、可变增益、低通、ADC 采样后送入 PRML(局部响应最大似然) 通道:

磁通翻转 → 磁头感应电压 → 预放 → VGA+低通 → ADC → PRML 均衡
   → Viterbi 最大似然译码 → 比特流 → ECC 纠错
PRML 眼图动画
图 4:PRML 眼图(动画)。多条叠加波形在采样点(绿线)处形成"眼睛";眼睛越窄代表码间串扰越重。PRML 不在每点硬判 0/1,而是用 Viterbi 在已知信道模型下找最似然比特序列。

记录密度高时相邻比特脉冲会码间串扰叠加。PRML 不逐点判 0/1,而是把整段采样序列送入 Viterbi 译码器,在已知信道模型下找最可能产生该波形的比特序列——这是容量持续翻倍的关键技术。现代盘 ECC 用 LDPC 码,可纠多位错误。

3.5 扇区内部结构

┌────────┬────────┬──────────┬──────────┬─────┐
│ Preamble│ Sync  │ 用户数据 │  ECC/CRC │ Gap │
│ 前导同步│ 标记  │  (512B)  │  校验码  │间隔 │
└────────┴────────┴──────────┴──────────┴─────┘
扇区内部结构动画
图 5:扇区内部结构(动画扫光)。一个扇区不只是 512B 用户数据,还含前导/同步/ECC/间隔;物理扇区常为 4KiB(512e/4Kn),跨扇区的 8KiB 页面写可能撕裂 (torn)。

ECC 失败,控制器重试;多次失败报 介质错误 (UNC)——数据库偶发的只读失败来源。

四、一次写入:写路径全景与 fsync 语义(DB 内核最核心)

写入前几步(寻道、旋转等待)与读取一致,差异在「把数据放上介质」:

flowchart TD
    A["主机: WRITE LBA n, 数据"] --> B["控制器: LBA 转物理位置"]
    B --> C["寻道 + 旋转等待"]
    C --> D["写电流磁化介质: 线圈磁场翻转剩磁"]
    D --> E["按编码写比特: 磁通翻转方向表示 0/1"]
    E --> F["追加 ECC + 前导/同步"]
    F --> G["回读校验 Read-After-Write"]
    G --> H["报告完成, 或重写/重映射坏块"]
写路径分层与 fsync 动画
图 6:写路径分层(动画)。蓝色数据包沿 ①→④ 异步流动(write() 进页缓存即返回);绿色 fsync 脉冲周期性把 ④ 盘缓存数据强制推到 ⑤ 盘片。红色 ②④ 为易失缓存。

下图用时序图对比”普通 write()“与”fsync 强制落盘”的差异:

sequenceDiagram
    participant App as "应用"
    participant PC as "页缓存(易失)"
    participant BL as "块层"
    participant DC as "盘缓存(易失)"
    participant PL as "盘片(非易失)"
    App->>PC: write() 拷贝即返回
    Note over PC,DC: 回写线程异步刷出
    PC->>BL: WRITE
    BL->>DC: 进缓存即 ACK
    App->>PC: fsync()
    PC->>BL: 强制刷出脏页
    BL->>DC: FLUSH CACHE / FUA
    DC->>PL: 真正落盘
    PL-->>App: durable commit

但 DB 内核真正要搞懂的,是write() 系统调用到数据真正掉不丢,中间经过了哪几层缓存——这才是持久化语义的全部真相。

4.1 一条写经历的完整层级

应用进程
  │ write()  ← 只把数据拷进内核页缓存, 立即返回!

内核页缓存 (Page Cache, 易失, 在主机内存)
  │ 由 pdflush/回写线程 异步刷出 (脏页超时/超阈值)

块层 (Block Layer / blk-mq)  ← IO 调度、合并、重排
  │ 下发 SCSI/SATA 命令 (WRITE)

硬盘 DRAM 写缓存 (易失!)  ← 数据进缓存即回 ACK, 随后异步落盘
  │ 由盘固件异步刷到盘片

盘片介质 (非易失, 真正持久)

关键点:write() 返回 ≠ 数据落盘。它只进了主机内存的页缓存。真正持久要经过:页缓存被回写线程刷出 → 块层 → 盘内 DRAM 缓存(依然易失)→ 盘片。中间任何一层掉电都可能丢数据。

4.2 fsync 到底做了什么

fsync(fd) 要保证持久,必须击穿两层易失缓存

  1. 刷主机页缓存:把该文件相关的脏页强制写回块层(绕过”异步回写”)。
  2. 刷盘内 DRAM 缓存:在命令里加 FLUSH CACHE(或对该次写用 FUA — Force Unit Access,绕过盘缓存直接落盘后才 ACK)。

只有二者都发生,事务提交才算 durable。如果盘缓存是易失的、且 fsync 没发 FLUSH/FUA,那么”已提交事务”其实只在盘的内存里,断电即丢——WAL 也救不回来,因为 WAL 记录本身也没真落盘。

4.3 写屏障与顺序保证

光”落盘”还不够,还要保证顺序:WAL 的提交记录必须先于数据页落盘,否则崩溃时可能看到”数据改了但 WAL 没记”。早期内核用 write barrier 强制这种顺序;现代 blk-mq 下,日志文件系统/数据库用「flush + FUA」显式冲刷来替代屏障。数据库内核要清楚:你的 fsync 顺序依赖,最终是靠底层 flush 命令的保序实现的。

4.4 非易失缓存(PLP)——“ACK 即持久”成立的条件

它是什么

PLP(Power-Loss Protection,掉电保护)是企业级 HDD / RAID 卡 / 企业 SSD 上的一项硬件兜底电路:在电路板上集成电容(或超级电容 / 电池),并配合专门固件逻辑。电容平时处于充电待命状态,它攒下的电能只用于一件事——断电后的”最后一刷”

其工作时序如下:外部供电一旦消失,盘立即检测到掉电 → 切换由电容储备电能供电 → 固件在电容维持的几十毫秒窗口内,把 DRAM 写缓存里尚未落盘的数据强制刷到非易失介质(HDD 刷到盘片,SSD 刷到 NAND)→ 数据落定后供电才彻底消失。

sequenceDiagram
    participant P as "外部供电"
    participant C as "电容(PLP)"
    participant D as "DRAM 写缓存(易失)"
    participant M as "非易失介质(盘片/NAND)"
    P->>C: 正常时持续给电容充电待命
    P--xP: 供电中断!
    C->>D: 电容接管供电(几十 ms 窗口)
    D->>M: 固件强制把缓存数据刷出
    M-->>C: 数据落定, 可安全掉电

它解决什么问题

普通消费级 HDD 的 DRAM 写缓存是易失的:开启 write-back 模式后,一次写只要进缓存就向主机回 ACK,数据随后才异步落盘。此时 fsync() 只保证数据”进了盘缓存”,并没进盘片。一旦这期间断电,缓存里的数据全部蒸发——事务已经向客户端报告”提交成功”,底层却实际丢失,直接违反 ACID 的 D(持久性)

因此没有 PLP 的盘,生产库要么关掉易失写缓存(write-through,每次写直落盘片、性能骤降),要么每次写都走 FUA / 写穿透。而有了 PLP,write-back 缓存就变得”安全”:断电时电容兜底把缓存刷盘,于是数据库可以一边开着写缓存享受高吞吐,一边保证已 ACK 的数据不丢。这正是企业盘能撑住 OLTP IOPS 的关键前提。

内核开发要诀:durable commit 的充要条件 = 关键写经 FUA/写穿透或 PLP 非易失缓存真正落盘 + flush 保序 + 上层 fsync() 真强制刷。WAL 提供的是崩溃重放能力,而非替代底层持久化——别把 WAL 当掉电保险丝。

4.5 实操:如何检测一块盘是否带 PLP

PLP 没有统一的标准探测位,但生产环境可用下面几条命令快速判断一块盘”到底有没有掉电保护”:

# 1) 内核视角:盘写缓存当前是 write_back(开了) 还是 write_through(写穿透)
cat /sys/block/sda/queue/write_cache

# 2) 多数企业盘会在 SMART 信息里自报 PLP 状态 (Seagate Exos / WD Gold / 等)
smartctl -a /dev/sda | grep -i "power loss"
#   命中示例:  Power Loss Protection: Enabled

# 3) SAS/SCSI 盘查看写缓存是否开启 (WCE 位)
sdparm --get=WCE /dev/sda

# 4) NVMe 盘:看 Volatile Write Cache 位 (vwc);PLP 一般由厂商工具/SMART 报告
nvme id-ctrl /dev/nvme0 | grep -i vwc

解读要点:

  • write_cache = write_back 且确认有 PLP → 可放心开缓存,兼顾高性能与”ACK 即持久”。
  • write_cache = write_back查不到 PLP(消费级盘常态)→ 视为易失,生产库必须走 FUA/写穿透,或显式关缓存:
    hdparm -W0 /dev/sda          # SATA 消费盘:关闭写缓存
    sdparm --set=WCE=0 /dev/sda  # SAS/SCSI 盘:关闭写缓存
  • write_cache = write_through → 已写穿透,每次写直达介质,安全但慢。

经验法则:拿不准就当没有 PLP。在无法 100% 确认带电容备份的盘上跑数据库,宁可牺牲一点写性能把易失写缓存关掉,也不要让”已提交事务”暴露在掉电丢失风险里。

五、SMR 叠瓦式记录:数据库工程师的隐形地雷

很多”数据库盘莫名周期性卡死”的案子,元凶是 SMR(叠瓦式磁记录)

5.1 原理

为提升密度,SMR 让相邻磁道像屋顶瓦片一样部分重叠:写一条磁道会破坏下游磁道的数据,因此必须连带着把下游所有磁道一起重写。

普通 PMR:   ┌──┐┌──┐┌──┐┌──┐   磁道互不重叠, 可随机写
            track0 track1 track2 track3

SMR 叠瓦:   ┌────┐
          ┌────┐│
        ┌────┐││   写 track1 必须重写 track2, track3... (RMW 风暴)
SMR 叠瓦重写级联动画
图 7:SMR 叠瓦重写级联(动画)。写入标橙的磁道 K 后,下游磁道(红箭头方向)被级联强制整区重写 (Read-Modify-Write);区内只能顺序写,随机改写即触发此风暴。

盘被分成若干 zone(区),区内只能顺序写,改写任意一处都要 RMW 整个下游——延迟从毫秒级跳到几百毫秒甚至秒级。

5.2 两类 SMR 对数据库的影响

类型管理方式数据库风险
DM-SMR(设备托管)固件偷偷做 RMW,对主机透明最危险:随机写时固件静默触发 RMW,尾延迟爆炸且不可预测
HA-SMR(主机感知)需 FS/应用按 zone 顺序写、显式 reset若数据库不感知 zone,写满即错;需专门适配

WAL 是顺序追加,理论上 SMR 还能忍;但 checkpoint、后台刷脏、Compaction、VACUUM 这些”改写随机位置”的操作会疯狂触发 RMW,造成周期性写入卡顿与极高尾延迟。

5.3 识别与规避

  • smartctl -i 看型号,查厂商是否标注 SMR(常见大容量冷存储盘、部分桌面盘)。
  • 生产数据库坚决不用消费级 SMR 盘;选 CMR/PMR 企业或近线盘。

六、NCQ、队列深度与 IO 调度(吞吐 vs 延迟的博弈)

6.1 NCQ 重排

NCQ (Native Command Queuing) 允许主机一次下发最多 32 个 IO,固件重排让磁头沿最短路径单向服务,减少来回摆动:

主机下发:  读磁道 100 → 50 → 200 → 30
NCQ 重排:  30 → 50 → 100 → 200   ← 磁头单向扫过

6.2 队列深度:不是越深越好

  • 过浅:磁头在一次 IO 间空隙无法被其他 IO 填补,吞吐上不去。
  • 过深:大量 IO 在盘内排队,每个 IO 的延迟被拉长(吞吐升、延迟炸)。数据库 OLTP 要低延迟,盲目加大队列深度会牺牲尾延迟。

6.3 Linux 块层调度器(blk-mq)

none          : 不做调度, 直接提交 (适合无机械seek的 NVMe; HDD 也可配但非最优)
mq-deadline   : 保证请求 deadline, HDD 随机/混合负载的稳妥默认
kyber         : 基于延迟目标动态限流, 适合混合负载
bfq           | 桌面/公平调度, 一般不用于数据库服务器

DB 内核视角:HDD 上常用 mq-deadline(兼顾吞吐与延迟上界);纯大块顺序 IO 可配 none;NVMe 默认 none。调度器影响”随机 IO 是否被合并/排序”,直接关系到寻道次数。

七、延迟、IOPS、吞吐与尾延迟

7.1 拆解公式

随机读延迟 ≈ 寻道(8ms) + 旋转(4.2ms) + 传输(<0.1ms) + 控制器/协议开销
           ≈ 12.25ms
随机读 IOPS ≈ 1 / 0.01225 ≈ 80 IOPS

对比 SSD 随机读 数万~数十万 IOPS,差 3 个数量级。

7.2 平均值会骗人:尾延迟与排队效应

数据库最怕的不是平均 12ms,而是 p99.9 可能到几百毫秒。原因在于排队:当 IO 到达率 λ 接近服务率 μ,等待时间按 M/M/1 直觉 W = 1/(μ−λ) 非线性飙升。HDD 的 μ 很低(约 80 IOPS),稍有并发队列就堵。这就是 HDD 不适合低延迟 OLTP 的根本原因——不是单次慢,而是一忙就雪崩

八、错误处理与数据完整性

  • UNC(不可纠正错误):扇区 ECC 失败,读请求返回错误。数据库层表现为偶发读失败——要靠 page checksum 在内存/落盘时检测,副本 / RAID 兜底。
  • ERC / TLER(错误恢复时限):企业盘固件把错误恢复限制在较短时间(如 7s)内,超时即把错误上交主机;消费盘可能重试几十秒。在 RAID 场景下,消费盘长时间”无响应”会被 RAID 卡误判为掉盘、触发重建风暴——数据库服务器要用带 ERC 的企业盘
  • 端到端保护(DIX/DIF):在 SCSI 层对数据加保护信息,防止”盘内正确但传输中损坏”,企业存储可选。

九、设备级读写放大(DB 写入成本的隐藏项)

来源放大机制DB 影响
512e 不对齐亚 4KiB / 跨边界写触发 RMW写延迟翻倍
SMR 叠瓦随机写触发下游整区 RMW尾延迟爆炸
小块写凑整写小于扇区被凑整到扇区写放大
WAL + 数据双写同一修改写 WAL 再写数据页逻辑放大(必要代价)

十、对数据库内核的综合启示

HDD 特性数据库内核应对
寻道/旋转占主导,随机极慢缓冲池把随机读转内存命中;B-Tree 索引减少磁盘访问次数
相邻 LBA 物理相邻聚簇/堆组织表、按主键顺序写减少磁头摆动
顺序写快WAL 顺序追加;批量导入 COPY;避免随机小写
512e 不对齐 RMW全栈 4KiB 对齐;页面尺寸按 4KiB 边界起址
写非原子 / torn pageWAL full-page write + page checksum 检测与恢复
盘缓存掉电易失FUA/写穿透 或 PLP 非易失缓存 + fsync() 真强制落盘 + flush 保序
SMR 随机写 RMW生产禁用消费级 SMR,选用 CMR 企业盘
NCQ 重排适度并发 IO 提升吞吐;控制队列深度保低延迟
低 μ + 排队雪崩HDD 只承载大块/顺序/离线负载;OLTP 低延迟诉求交给 SSD
介质错误/静默损坏page checksum、副本、RAID;企业盘开 ERC

一句话总结:数据库内核对 HDD 的全部优化,都源于两个事实——机械访问极慢且非原子,以及 “写入返回”默认不保证持久。把它们想清楚,你就能解释为什么早期数据库把”避免随机磁盘访问”当头等大事,为什么 WAL、缓冲池、聚簇、索引、checkpoint 这些机制长这样;而即便换了 SSD,“顺序优于随机""写必须显式落盘”的直觉依然成立,只是代价小了百倍。

参考