MySQL/InnoDB 内核

MySQL 的事务存储引擎——B+Tree 索引、MVCC 多版本并发、缓冲池、Redo/Undo 日志。

概念

InnoDB 是 MySQL 的默认存储引擎,也是研究”如何构建 ACID 事务系统”的最佳教材。它解决了数据库最核心的两个问题:1) 如何组织海量数据使其可快速检索 (B+Tree);2) 如何在并发更新时保证一致性 (MVCC + Redo/Undo)。InnoDB 的内核代码约 40 万行 C/C++。

核心组件

组件职责关键概念
B+Tree 索引聚集索引 (主键) + 二级索引页 (16KB), 填充因子
缓冲池 (Buffer Pool)热数据的内存缓存LRU list, 脏页刷新
MVCC读写不互斥的多版本并发控制ReadView, undo log 链
Redo Log已提交事务的日志 (崩溃恢复)循环写, 顺序 I/O
Undo Log事务回滚 + MVCC 历史版本undo segment
隔离级别RU / RC / RR / Serializable间隙锁, next-key lock
查询优化器SQL → 执行计划, 索引选择成本估算

B+Tree 存储架构

B+Tree (与 B-Tree 的关键区别):
    所有数据只存储在叶子节点
    内部节点仅存储 key + 子页指针 (导航用)
    叶子节点之间用双向链表连接 (范围查询)

表 "users" (InnoDB 聚集索引):
    Interior Page (内部节点 16KB):
        [Infimum] [key:10, page:4] [key:30, page:5] [key:50, page:6] [Supremum]

    Leaf Page (叶子节点, 含完整数据):
        <--prev--| [row(10): {name:"A", age:22}] [row(20): {name:"B", age:30}] ... |--next-->

    二级索引 (例如 idx_age):
        叶子节点存储: {age, 主键值}
        回表查询: 从二级索引得到主键 → 用主键去聚集索引查完整数据

页内结构:
    +-------------------+-----------+--------------+---------------------+
    | File Header (38B) | Infimum + | User Records | Free Space →        |
    |                   | Supremum  | (数据记录)   |                     |
    +-------------------+-----------+--------------+---------------------+
    | Page Directory    | File Trailer (8B)                            |
    +-------------------+-----------------------------------------------+

MVCC (多版本并发控制)

MVCC 核心规则:
    每行记录有 2 个隐藏列:
        DB_TRX_ID: 最后一次修改该行的事务 ID
        DB_ROLL_PTR: undo log 回滚指针

    读操作 (SELECT):
        生成 ReadView: 记录当前活跃事务 ID 列表
        读取一行时:
            IF 该行 DB_TRX_ID < ReadView.min_trx_id:
                可见 (事务在 ReadView 之前已提交)
            ELSE IF 该行 DB_TRX_ID == 当前事务 ID:
                可见 (自己的修改)
            ELSE:
                不可见 → 沿 DB_ROLL_PTR 找到历史版本
                        直到找到可见的版本

    写操作 (UPDATE):
        1. 写 undo log: 记录修改前的旧值
        2. 修改缓冲池中的页 (标记为脏)
        3. 写 redo log: 记录 "在页 X 偏移 Y 处将 A 改为 B"
        4. DB_TRX_ID = 当前事务 ID

Redo Log + Undo Log 协同

事务提交:
    1. 写 undo log (记录旧值, 可能用于回滚)
    2. 修改缓冲池中的数据页 (脏页)
    3. 写 redo log buffer (在内存中)
    4. 事务提交 (COMMIT):
       a. redo log buffer 写盘 (redo log 文件, 顺序追加)
       b. 脏页仍在缓冲池中, 不立刻写盘 (异步刷盘)
    5. 崩溃恢复:
       将 redo log 应用于数据页 → 保证已提交事务不丢失
       未提交事务的 undo log 回滚 → 保证原子性

    这是 Write-Ahead Logging (WAL) 的具体应用

隔离级别

隔离级别脏读不可重复读幻读加锁策略
READ UNCOMMITTEDYYY几乎不加锁
READ COMMITTEDNYY行级锁, 每个语句生成新 ReadView
REPEATABLE READNNY (MVCC 解决部分)行级锁 + gap lock, 一个事务一个 ReadView
SERIALIZABLENNN全表锁, 串行执行