b+树叶子节点通过页头page_list_node的prev/next指针物理相连,形成按键值升序排列的双向链表,仅限叶子层,支持高效范围查询与顺序扫描。

MySQL 的 B+ 树中,叶子节点之间通过双向链表指针物理相连,这是 InnoDB 存储引擎在页(page)层面实现的硬链接结构。
具体来说:
-
每个叶子节点对应一个 16KB 的数据页(Page),页头(page header)中包含两个关键字段:
-
PAGE_LAST_INSERT(不相关) -
PAGE_LEVEL= 0(标识该页是叶子层) - 更重要的是:页头里有
PAGE_BTR_SEG_LEAF相关的段信息,而页尾(page trailer)或页内系统字段中,实际维护着两个指针:-
PAGE_LIST_NODE结构,含prev和next两个 4 字节(或 8 字节,取决于版本)偏移量,指向前一个和后一个叶子页的页号(page no)。
-
- 这些指针不是逻辑索引值,而是直接指向磁盘上相邻叶子页的物理位置(即 space_id + page_no)。
-
-
链表顺序严格按键值升序排列:
- 所有叶子页中的记录按主键(或索引键)有序存放;
- 链表的
next方向对应键值递增方向,prev对应递减方向; - 因此
SELECT * FROM t ORDER BY id或WHERE id BETWEEN 100 AND 200可以从定位到的起始页开始,连续读取next页,无需反复回树根查找。
-
链表只存在于同一层级的叶子节点之间:
- 非叶子节点(internal nodes)之间没有链表;
- 它们靠父节点中的键值和子页指针导航;
- 只有叶子层构成完整、可遍历的双向链表,支撑高效范围扫描和全索引遍历。
-
插入/分裂时自动维护链表:
- 当叶子页满,发生页分裂(page split)时,InnoDB 不仅拆分记录,还会重写前后页的
prev/next指针; - 例如:原链为 A ⇄ B ⇄ C,B 页分裂出新页 D,则链表更新为 A ⇄ B ⇄ D ⇄ C,并更新各页头中的指针值;
- 这个过程由存储引擎原子完成,对用户透明。
- 当叶子页满,发生页分裂(page split)时,InnoDB 不仅拆分记录,还会重写前后页的
-
注意:链表连接的是页(page),不是单条记录;
- 页内记录用单向链表(record heap + next_record offset)组织;
- 页间用双向链表(page level)组织——这是两个不同层级的链表设计。
这样设计让 MySQL 能在一次定位后,用最少的随机 I/O,靠顺序 I/O 批量拉取多个叶子页,配合操作系统预读(read-ahead),大幅提升范围查询吞吐。











