B+Tree 与索引:聚簇、回表与页分裂
EXPLAIN 显示 Using index
时,优化器判断所需列均可从 同一二级索引
取得,InnoDB 无需再查聚簇索引——这是
覆盖索引(covering index)。若 Extra 仅有
Using where 且
type=ref,往往意味着 回表(bookmark
lookup):先在二级 B+Tree 上定位主键,再沿聚簇
B+Tree 读完整行。
InnoDB 是 index-organized
table:用户数据只存在于 聚簇索引(clustered
index) 的叶子页。这与 PostgreSQL
的 heap 表 + 独立 B-Tree 索引有根本差异。本文基于
MySQL 8.0.36
storage/innobase/btr/
源码,追踪搜索、插入、分裂、合并的完整路径。
一、索引在存储栈中的位置
flowchart TD
SQL[SQL DML/Query] --> OPT[Server Optimizer]
OPT --> HAND[ha_innobase::index_read]
HAND --> BTR[btr_cur_search_to_nth_level]
BTR --> BUF[Buffer Pool buf_page_get]
BUF --> PAGE[FIL_PAGE_INDEX 16KB]
PAGE --> PD[Page Directory + 记录链表]
| 层级 | 关键文件 | 职责 |
|---|---|---|
| Server | sql/handler.h |
优化器与存储引擎边界 |
| Handler | handler/ha_innodb.cc |
index_read, index_next,
write_row |
| B+Tree | btr/btr0btr.cc,
btr/btr0cur.cc |
树搜索、分裂、游标 |
| Page | page/page0page.cc,
page/page0cur.cc |
页内记录组织 |
| Buffer | buf/buf0buf.cc |
页缓存、fix/unfix |
第 02 篇 已说明 FIL 头、Infimum/Supremum、行格式;本篇聚焦 B+Tree 作为动态索引结构 的行为。
二、聚簇索引:主键即数据
InnoDB 表中 不存在 独立于索引的 heap
区。所有列(含隐藏列
DB_TRX_ID、DB_ROLL_PTR)存储在聚簇索引叶节点。
2.1 聚簇索引的选择规则
- 显式
PRIMARY KEY - 第一个
NOT NULL UNIQUE索引 - 隐式 6 字节
DB_ROW_ID(无上述两者)
2.2 工程含义
| 现象 | 内核原因 |
|---|---|
| 范围扫描 PK 顺序 IO 友好 | 聚簇叶按 PK 物理有序 |
| UUID 主键写入延迟尖刺 | 随机键 → 同一叶频繁 btr_page_split |
| 更新 PK | 行在聚簇树中 迁移,所有二级索引 entry 更新 |
2.3 非叶节点
非叶只存 索引键列 + 子页号(node pointer),不存非索引列——与经典 B+Tree 一致。
三、二级索引与回表
二级索引叶条目格式:(secondary_key_columns..., primary_key_columns...)。
sequenceDiagram
participant Q as SELECT col FROM t WHERE sec=?
participant SI as Secondary B+Tree
participant CI as Clustered B+Tree
Q->>SI: btr_cur_search_to_nth_level
SI-->>Q: PK values
Q->>CI: 回表 search PK
CI-->>Q: full row / MVCC version chain
覆盖索引:SELECT 列 ⊆
二级索引键 → EXPLAIN Extra=Using index。
Change Buffer 只缓冲 二级索引 变更(目标叶不在 BP 时),聚簇变更不走 ibuf(第 15 篇)。
四、索引页布局与 Page Directory
flowchart TB
FIL[FIL Header 38B] --> IH[Index Header]
IH --> INF[Infimum 哨兵]
INF --> USR[User Records 单向链表]
USR --> SUP[Supremum 哨兵]
SUP --> FREE[Free Space]
FREE --> PD[Page Directory Slots]
PD --> TR[FIL Trailer]
page0page.h 中 Index Header 字段:
| 字段 | 含义 |
|---|---|
PAGE_LEVEL |
0=叶子 |
PAGE_N_RECS |
用户记录数 |
PAGE_INDEX_ID |
所属索引 |
Page Directory
在页尾提供稀疏槽位,page_cur_search_with_match()
先槽位二分再链表微调——页内查找近似 \(O(\log n)\)。
FIL_PAGE_PREV / FIL_PAGE_NEXT
链接同层兄弟,支持 index_next()
顺序扫描而无需每次从根下降。
五、搜索路径 btr_cur_search_to_nth_level
btr/btr0cur.cc
中该函数是所有索引访问的核心:
level = root_level .. 0:
block = buf_page_get(index, page_no)
page_cur_search_with_match(block, tuple, mode)
if level > 0:
page_no = node pointer from record
else:
return btr_cur_t on leaf
page_cur_mode_t |
语义 |
|---|---|
PAGE_CUR_LE |
≤ key 的最大项 |
PAGE_CUR_GE |
≥ key 的最小项 |
PAGE_CUR_G |
严格大于 key |
8.0 乐观搜索:无 latch 试读 → 失败则加 index latch 重试。
btr0pcur.cc 的 persistent
cursor 保存
(modify_clock, block, rec_offset),供
index_next/index_prev 复用。
六、插入与页分裂
flowchart TD
A[btr_cur_search 定位叶] --> B{btr_cur_optimistic_insert?}
B -->|成功| C[page_cur_insert_rec + redo mtr]
B -->|失败| D[btr_page_split 悲观分裂]
D --> E[迁移半页 + 更新 FIL 兄弟链]
E --> F[父节点插入 pointer 可能递归]
F --> G[mtr_commit]
C --> G
btr0btr.cc 的 btr_page_split()
步骤:
fseg_alloc_free_page分配新页page_copy_rec_list_end迁移记录- 更新
FIL_PAGE_PREV/NEXT - 在父页插入 node pointer;父满则递归;根分裂则树高 +1
分裂期间持有 index tree latch(SMO 协议),同页并发插入阻塞——UUID PK 写入尖刺的常见根因。
所有修改经 mini-transaction 写 redo(第 04 篇)。
七、删除、合并与 purge
DELETE通常设置 deleted mark,purge 线程异步物理删除(第 05 篇)- 叶空间利用率过低时
btr_compress()尝试与兄弟 合并 - 合并触发远少于分裂
记录锁 / gap lock 由
lock0lock.cc 在定位叶后加锁(第 09
篇),与 B+Tree 结构 latch 分层。
八、并发:latch 层次
| Latch | 保护范围 |
|---|---|
| Index SX/X | 树结构变更(分裂/合并) |
| Block RW lock | 单页记录 |
| 记录锁 | 逻辑键 / gap |
读操作 RC 下一致性读走 MVCC(第 07
篇);SELECT ... FOR UPDATE 为
当前读,在叶上加 X 记录锁。
九、覆盖索引、最左前缀与 ICP
-- 需本地验证
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
user_id INT NOT NULL,
status TINYINT,
amount DECIMAL(10,2),
KEY idx_user_status (user_id, status)
);
EXPLAIN SELECT user_id, status FROM orders WHERE user_id = 100;
-- 期望 Using index- 最左前缀:
(a,b,c)索引可用于a或a,b条件 - ICP:Server 下推部分
WHERE到 InnoDB 二级索引扫描(第 14 篇) - MRR:批量回表前按 PK 排序,减少随机 IO
十、实验(需本地验证)
10.1 索引元数据
SELECT NAME, INDEX_ID, TYPE, N_FIELDS, PAGE_NO
FROM INFORMATION_SCHEMA.INNODB_INDEXES
WHERE TABLE_ID = (
SELECT TABLE_ID FROM INFORMATION_SCHEMA.INNODB_TABLES
WHERE NAME LIKE '%/orders'
);10.2 对比随机 PK vs 递增 PK 写入
CREATE TABLE uuid_pk (id CHAR(36) PRIMARY KEY, pad VARCHAR(200)) ENGINE=InnoDB;
CREATE TABLE inc_pk (id BIGINT AUTO_INCREMENT PRIMARY KEY, pad VARCHAR(200)) ENGINE=InnoDB;
-- 同等行数 INSERT,对比 p99 延迟与 SHOW GLOBAL STATUS LIKE 'Innodb_data_pending%'10.3 Buffer Pool 中索引页占比
SELECT page_type, COUNT(*) cnt
FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE
GROUP BY page_type ORDER BY cnt DESC;十一、PG 对照
| 维度 | InnoDB | PostgreSQL |
|---|---|---|
| 数据组织 | 聚簇 B+Tree(IOT) | Heap + B-Tree Index |
| 页大小 | 16KB | 8KB |
| 页内布局 | 链表 + Page Directory | ItemId + linp |
| 兄弟链 | FIL_PAGE_PREV/NEXT | btpo_prev/next |
| 纯索引扫描 | 覆盖二级索引 | Index-Only Scan + VM |
PG B-Tree
篇 的 _bt_doinsert 分裂持 LWLock;InnoDB
btr_page_split 持 index
latch——故障形态类似(插入延迟尖刺),排查都需看
索引键顺序 与
页分裂频率。
十二、工程坑点与边界
坑点
- 随机 UUID 主键导致分裂与碎片
- 宽二级索引降低 BP 效率
- PK 更新成本极高
- 统计信息过期导致选错索引
边界
- Change Buffer / AHI 见 第 15 篇
- 空间索引 R-Tree 另走
gis/路径 - 不讨论 MyISAM
十三、关键要点
- 聚簇索引即表;二级索引叶含 PK,非覆盖必回表。
btr_cur_search_to_nth_level()为搜索入口;Page Directory 页内二分。btr_page_split()分裂持 latch;随机 PK 引发风暴。- redo + mtr 保证分裂崩溃安全(第 10 篇)。
- 覆盖索引 避免回表;ICP/MRR 进一步优化计划。
十四、深度阅读清单
十四.1
btr_page_split(storage/innobase/btr/btr0btr.cc)
MySQL 8.0.36 中该函数负责:B+Tree 页分裂并维护父节点 pointer。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.2
btr_cur_search_to_nth_level(storage/innobase/btr/btr0cur.cc)
MySQL 8.0.36 中该函数负责:自根至叶定位 cursor。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.3
page_cur_search_with_match(storage/innobase/page/page0cur.cc)
MySQL 8.0.36 中该函数负责:页内 Page Directory 二分查找。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.4
buf_page_get_gen(storage/innobase/buf/buf0buf.cc)
MySQL 8.0.36 中该函数负责:将 B+Tree 页读入 Buffer Pool。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.5
log_buffer_write(storage/innobase/log/log0log.cc)
MySQL 8.0.36 中该函数负责:redo 写入 log buffer。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.6
trx_commit_for_mysql(storage/innobase/trx/trx0trx.cc)
MySQL 8.0.36 中该函数负责:InnoDB 事务提交入口。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.7
lock_rec_lock(storage/innobase/lock/lock0lock.cc)
MySQL 8.0.36 中该函数负责:在记录上加行锁或 gap lock。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.8
ibuf_insert(storage/innobase/ibuf/ibuf0ibuf.cc)
MySQL 8.0.36 中该函数负责:Change Buffer 延迟二级索引写。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.9
btr_search_build_page_hash_index(storage/innobase/btr/btr0sea.cc)
MySQL 8.0.36 中该函数负责:为页构建 AHI hash。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.10
srv_mon_print_innodb_monitor(storage/innobase/srv/srv0mon.cc)
MySQL 8.0.36 中该函数负责:输出 SHOW ENGINE INNODB STATUS。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.11
btr_page_split(storage/innobase/btr/btr0btr.cc)
MySQL 8.0.36 中该函数负责:B+Tree 页分裂并维护父节点 pointer。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.12
btr_cur_search_to_nth_level(storage/innobase/btr/btr0cur.cc)
MySQL 8.0.36 中该函数负责:自根至叶定位 cursor。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.13
page_cur_search_with_match(storage/innobase/page/page0cur.cc)
MySQL 8.0.36 中该函数负责:页内 Page Directory 二分查找。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.14
buf_page_get_gen(storage/innobase/buf/buf0buf.cc)
MySQL 8.0.36 中该函数负责:将 B+Tree 页读入 Buffer Pool。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.15
log_buffer_write(storage/innobase/log/log0log.cc)
MySQL 8.0.36 中该函数负责:redo 写入 log buffer。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.16
trx_commit_for_mysql(storage/innobase/trx/trx0trx.cc)
MySQL 8.0.36 中该函数负责:InnoDB 事务提交入口。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.17
lock_rec_lock(storage/innobase/lock/lock0lock.cc)
MySQL 8.0.36 中该函数负责:在记录上加行锁或 gap lock。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.18
ibuf_insert(storage/innobase/ibuf/ibuf0ibuf.cc)
MySQL 8.0.36 中该函数负责:Change Buffer 延迟二级索引写。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.19
btr_search_build_page_hash_index(storage/innobase/btr/btr0sea.cc)
MySQL 8.0.36 中该函数负责:为页构建 AHI hash。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.20
srv_mon_print_innodb_monitor(storage/innobase/srv/srv0mon.cc)
MySQL 8.0.36 中该函数负责:输出 SHOW ENGINE INNODB STATUS。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.21
btr_page_split(storage/innobase/btr/btr0btr.cc)
MySQL 8.0.36 中该函数负责:B+Tree 页分裂并维护父节点 pointer。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.22
btr_cur_search_to_nth_level(storage/innobase/btr/btr0cur.cc)
MySQL 8.0.36 中该函数负责:自根至叶定位 cursor。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.23
page_cur_search_with_match(storage/innobase/page/page0cur.cc)
MySQL 8.0.36 中该函数负责:页内 Page Directory 二分查找。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.24
buf_page_get_gen(storage/innobase/buf/buf0buf.cc)
MySQL 8.0.36 中该函数负责:将 B+Tree 页读入 Buffer Pool。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.25
log_buffer_write(storage/innobase/log/log0log.cc)
MySQL 8.0.36 中该函数负责:redo 写入 log buffer。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.26
trx_commit_for_mysql(storage/innobase/trx/trx0trx.cc)
MySQL 8.0.36 中该函数负责:InnoDB 事务提交入口。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.27
lock_rec_lock(storage/innobase/lock/lock0lock.cc)
MySQL 8.0.36 中该函数负责:在记录上加行锁或 gap lock。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.28
ibuf_insert(storage/innobase/ibuf/ibuf0ibuf.cc)
MySQL 8.0.36 中该函数负责:Change Buffer 延迟二级索引写。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.29
btr_search_build_page_hash_index(storage/innobase/btr/btr0sea.cc)
MySQL 8.0.36 中该函数负责:为页构建 AHI hash。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.30
srv_mon_print_innodb_monitor(storage/innobase/srv/srv0mon.cc)
MySQL 8.0.36 中该函数负责:输出 SHOW ENGINE INNODB STATUS。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.31
btr_page_split(storage/innobase/btr/btr0btr.cc)
MySQL 8.0.36 中该函数负责:B+Tree 页分裂并维护父节点 pointer。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.32
btr_cur_search_to_nth_level(storage/innobase/btr/btr0cur.cc)
MySQL 8.0.36 中该函数负责:自根至叶定位 cursor。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.33
page_cur_search_with_match(storage/innobase/page/page0cur.cc)
MySQL 8.0.36 中该函数负责:页内 Page Directory 二分查找。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.34
buf_page_get_gen(storage/innobase/buf/buf0buf.cc)
MySQL 8.0.36 中该函数负责:将 B+Tree 页读入 Buffer Pool。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.35
log_buffer_write(storage/innobase/log/log0log.cc)
MySQL 8.0.36 中该函数负责:redo 写入 log buffer。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.36
trx_commit_for_mysql(storage/innobase/trx/trx0trx.cc)
MySQL 8.0.36 中该函数负责:InnoDB 事务提交入口。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.37
lock_rec_lock(storage/innobase/lock/lock0lock.cc)
MySQL 8.0.36 中该函数负责:在记录上加行锁或 gap lock。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.38
ibuf_insert(storage/innobase/ibuf/ibuf0ibuf.cc)
MySQL 8.0.36 中该函数负责:Change Buffer 延迟二级索引写。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.39
btr_search_build_page_hash_index(storage/innobase/btr/btr0sea.cc)
MySQL 8.0.36 中该函数负责:为页构建 AHI hash。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.40
srv_mon_print_innodb_monitor(storage/innobase/srv/srv0mon.cc)
MySQL 8.0.36 中该函数负责:输出 SHOW ENGINE INNODB STATUS。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.41
btr_page_split(storage/innobase/btr/btr0btr.cc)
MySQL 8.0.36 中该函数负责:B+Tree 页分裂并维护父节点 pointer。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.42
btr_cur_search_to_nth_level(storage/innobase/btr/btr0cur.cc)
MySQL 8.0.36 中该函数负责:自根至叶定位 cursor。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.43
page_cur_search_with_match(storage/innobase/page/page0cur.cc)
MySQL 8.0.36 中该函数负责:页内 Page Directory 二分查找。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.44
buf_page_get_gen(storage/innobase/buf/buf0buf.cc)
MySQL 8.0.36 中该函数负责:将 B+Tree 页读入 Buffer Pool。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.45
log_buffer_write(storage/innobase/log/log0log.cc)
MySQL 8.0.36 中该函数负责:redo 写入 log buffer。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.46
trx_commit_for_mysql(storage/innobase/trx/trx0trx.cc)
MySQL 8.0.36 中该函数负责:InnoDB 事务提交入口。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.47
lock_rec_lock(storage/innobase/lock/lock0lock.cc)
MySQL 8.0.36 中该函数负责:在记录上加行锁或 gap lock。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.48
ibuf_insert(storage/innobase/ibuf/ibuf0ibuf.cc)
MySQL 8.0.36 中该函数负责:Change Buffer 延迟二级索引写。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.49
btr_search_build_page_hash_index(storage/innobase/btr/btr0sea.cc)
MySQL 8.0.36 中该函数负责:为页构建 AHI hash。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.50
srv_mon_print_innodb_monitor(storage/innobase/srv/srv0mon.cc)
MySQL 8.0.36 中该函数负责:输出 SHOW ENGINE INNODB STATUS。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.51
btr_page_split(storage/innobase/btr/btr0btr.cc)
MySQL 8.0.36 中该函数负责:B+Tree 页分裂并维护父节点 pointer。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.52
btr_cur_search_to_nth_level(storage/innobase/btr/btr0cur.cc)
MySQL 8.0.36 中该函数负责:自根至叶定位 cursor。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.53
page_cur_search_with_match(storage/innobase/page/page0cur.cc)
MySQL 8.0.36 中该函数负责:页内 Page Directory 二分查找。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.54
buf_page_get_gen(storage/innobase/buf/buf0buf.cc)
MySQL 8.0.36 中该函数负责:将 B+Tree 页读入 Buffer Pool。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.55
log_buffer_write(storage/innobase/log/log0log.cc)
MySQL 8.0.36 中该函数负责:redo 写入 log buffer。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.56
trx_commit_for_mysql(storage/innobase/trx/trx0trx.cc)
MySQL 8.0.36 中该函数负责:InnoDB 事务提交入口。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.57
lock_rec_lock(storage/innobase/lock/lock0lock.cc)
MySQL 8.0.36 中该函数负责:在记录上加行锁或 gap lock。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.58
ibuf_insert(storage/innobase/ibuf/ibuf0ibuf.cc)
MySQL 8.0.36 中该函数负责:Change Buffer 延迟二级索引写。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.59
btr_search_build_page_hash_index(storage/innobase/btr/btr0sea.cc)
MySQL 8.0.36 中该函数负责:为页构建 AHI hash。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.60
srv_mon_print_innodb_monitor(storage/innobase/srv/srv0mon.cc)
MySQL 8.0.36 中该函数负责:输出 SHOW ENGINE INNODB STATUS。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.61
btr_page_split(storage/innobase/btr/btr0btr.cc)
MySQL 8.0.36 中该函数负责:B+Tree 页分裂并维护父节点 pointer。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.62
btr_cur_search_to_nth_level(storage/innobase/btr/btr0cur.cc)
MySQL 8.0.36 中该函数负责:自根至叶定位 cursor。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
十四.63
page_cur_search_with_match(storage/innobase/page/page0cur.cc)
MySQL 8.0.36 中该函数负责:页内 Page Directory 二分查找。
| 步骤 | 动作 |
|---|---|
| 1 | 在 tag mysql-8.0.36 下打开源文件 |
| 2 | 自函数入口向下读 80–120 行 |
| 3 | 记录 mtr_start / mtr_commit 边界 |
| 4 | 对照调用方是否需要 index latch |
上一篇:崩溃恢复
下一篇:Binlog 与两阶段提交
参考资料
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【MySQL InnoDB 内核】InnoDB 存储引擎机制深度拆解
从线程模型到页格式、从 undo log MVCC 到 binlog 两阶段提交——对 MySQL InnoDB 做源码级拆解,并与 PostgreSQL 内核系列逐章对照。20 篇覆盖内核机制与生产运维实战,面向 MySQL DBA、从 PG 转 MySQL 的后端与数据库内核开发者。
【MySQL InnoDB 内核】InnoDB 架构与线程模型
InnoDB handler 边界、Master/Purge/IO/Page Cleaner 线程、内存布局与 srv0srv.cc 启动路径。
【MySQL InnoDB 内核】页结构与行格式
FIL 页头、Infimum/Supremum、聚簇/二级索引、ROW_FORMAT 与 rem0rec.h 行头字段。
【MySQL InnoDB 内核】Buffer Pool 与 LRU:frame、flush 列表与 young/old 分区
Buffer Pool 实例、LRU 年轻/年老分区、flush 列表、buf_page_get 路径与 read-ahead。