20250713 页的文件头中最小值和最大值的作用
20250713 页的文件头中最小值和最大值的作用
问题:页内是单链表,为什么记录了最小值,还要记录最大值,哪怕记录了最大值,也没法从最大值那反过来访问最大值的地址啊
在数据库存储引擎(如InnoDB)的页结构中,记录 最小记录(Infimum) 和 最大记录(Supremum) 是出于 逻辑边界定义 和 遍历优化 的需要,而非单纯为了双向访问。以下是详细解释:
1. 最小记录(Infimum)和最大记录(Supremum)的作用
(1) 逻辑边界标记
- Infimum:
表示页中理论上的最小记录,比所有实际存储的记录都小(类似数学中的“负无穷”)。 - Supremum:
表示页中理论上的最大记录,比所有实际存储的记录都大(类似“正无穷”)。
为什么需要它们?
- 统一遍历逻辑:无论页内是否有数据,所有记录都位于
Infimum和Supremum之间,避免遍历时的边界条件判断。 - 简化插入/删除操作:新记录插入时,可直接通过比较确定位置,无需特殊处理“第一条”或“最后一条”记录。
(2) 页内单向链表的访问优化
虽然页内记录是单向链表(通过 next_record 指针链接),但 Infimum 和 Supremum 的存在使得:
- 从头到尾遍历:从
Infimum开始,沿next_record逐个访问,直到Supremum结束。 - 范围查询优化:
例如查询WHERE id > 100,数据库可以快速定位到第一个id > 100的记录,然后单向遍历到Supremum,无需关心链表尾部。
2. 为什么不需要通过最大值反向访问?
- 单向链表的设计初衷:
InnoDB的页内记录单向链表是为了高效顺序访问(如范围查询、全表扫描),而非随机反向遍历。- 若需要逆序访问,可通过索引的双向链表(如B+树叶子节点之间的
prev_page/next_page指针)实现跨页遍历。
- 若需要逆序访问,可通过索引的双向链表(如B+树叶子节点之间的
- 空间效率:
每个记录增加反向指针会额外占用存储空间(尤其是行数多的页),而实际业务中逆序访问页内记录的需求极少。
3. 实际页结构示例
以下是InnoDB页内记录的简化布局(单向链表):
text
Infimum (最小记录) → Record1 → Record2 → ... → RecordN → Supremum (最大记录)
- 所有操作均从
Infimum开始:- 插入新记录时,从
Infimum出发找到正确位置。 - 删除记录时,直接更新前驱记录的
next_record指针。
- 插入新记录时,从
4. 与B+树索引的关系
- 页内单向链表:负责页内记录的顺序管理。
- B+树叶子节点双向链表:负责跨页遍历(如
ORDER BY id DESC可通过prev_page指针逆序扫描)。
这种分工既节省了页内空间,又满足了全局查询需求。
5. 极端情况下的验证
假设不存储 Supremum:
- 插入最大值记录时:需特殊判断链表末尾,增加代码复杂度。
- 范围查询到页末尾:需额外检查是否已遍历完所有记录。
而Supremum的存在使得逻辑始终一致:
sql
-- 查询页内所有id > 100的记录
current = Infimum.next_record;
while (current != Supremum) {
if (current.id > 100) return current;
current = current.next_record;
}
总结
这种设计在 空间效率 和 操作一致性 之间取得了最佳平衡。
| 设计选择 | 原因 |
|---|---|
| 记录Infimum | 提供逻辑起点,统一插入/删除/遍历操作。 |
| 记录Supremum | 提供逻辑终点,避免遍历时的边界判断。 |
| 不存储反向指针 | 逆序访问需求少,优先节省空间;跨页逆序通过B+树叶子节点双向链表实现。 |
问题2:可是b+树不是以页内记录最大最小值来分页的吗,如果统一用正负无穷来包围了最大最小值,那怎么分页呢
这里涉及到 B+树的分裂逻辑 和 页内Infimum/Supremum的虚拟性 之间的区别。虽然页内用Infimum和Supremum标记逻辑边界,但B+树的分页依然依赖实际数据的最大/最小值。以下是详细解释:
1. Infimum/Supremum 是页内的逻辑标记
-
作用:仅为页内记录的遍历提供统一的起止点(类似链表的头尾哨兵节点),不参与B+树的分层决策。
-
虚拟性:
-
Infimum和Supremum是固定的伪记录(不存储实际数据)。 -
例如:即使页内只有一条记录
(id=5),逻辑上仍表示为:text
Infimum → (id=5) → Supremum
-
2. B+树的分页依赖实际数据的极值
B+树的分裂和层级划分基于页内实际存储数据的最大/最小值,而非Infimum/Supremum。具体逻辑如下:
(1) 非叶子节点的分页
- 非叶子节点存储的是子节点的最小键值(或范围边界)。
- 例如:某非叶子节点条目为
[10, 20, 30],表示:- 第一个子节点中的所有数据
< 10 - 第二个子节点中的数据
>= 10 且 < 20 - 第三个子节点中的数据
>= 20 且 < 30
- 第一个子节点中的所有数据
- 例如:某非叶子节点条目为
(2) 叶子节点的分页
- 叶子节点之间通过双向链表连接,且每个叶子节点存储实际数据。
- 分裂条件:当页内实际数据超过大小时(如16KB),按以下步骤分裂:
- 找到页内实际数据的中间值(如原页有数据
[5,8,10,15,20],中间值为10)。 - 新页包含
[10,15,20],原页保留[5,8]。 - 更新父节点:将新页的最小值(
10)插入父节点,作为分界键。
- 找到页内实际数据的中间值(如原页有数据
(3) 关键区别
| 对比项 | Infimum/Supremum | B+树分页依据 |
|---|---|---|
| 用途 | 页内遍历的边界哨兵 | 树形结构的层级划分 |
| 是否存储数据 | 否(虚拟记录) | 是(实际数据的最小/最大值) |
| 影响范围 | 仅限当前页 | 全局索引结构 |
3. 示例:B+树分裂与Infimum/Supremum无关
假设一个叶子页已满,实际数据为 [3,5,8,10,15](逻辑结构为 Infimum→3→5→8→10→15→Supremum):
- 分裂时:
- 选择中间值(如
8),新页存储[8,10,15],原页保留[3,5]。 - 父节点新增键值
8(新页的最小实际数据),而非Supremum。
- 选择中间值(如
- 分裂后:
- 新页的逻辑结构仍是
Infimum→8→10→15→Supremum。 - 原页的逻辑结构变为
Infimum→3→5→Supremum。
- 新页的逻辑结构仍是
Infimum/Supremum始终存在,但分裂完全依赖实际数据。
4. 为什么设计如此?
- 职责分离:
Infimum/Supremum解决页内操作一致性问题(如插入/删除无需判空)。- B+树的分页解决数据分布与查询效率问题。
- 性能优化:
- 实际数据的极值可直接用于索引定位,而虚拟记录无实际查询意义。
5. 总结
- Infimum/Supremum 是页内的“脚手架”,仅用于简化页内链表操作。
- B+树的分页 基于页中实际数据的物理极值,二者互不干扰。
- 类比:
Infimum/Supremum类似书页的“页眉/页脚”,而B+树的分页是图书馆的“书架分类规则”——书架不会因为页眉写了个“Chapter 1”就把书分成两半。
这种设计确保了高效的单页操作与全局索引结构的解耦!
6. 结论
Infimum/Supremum解决页内操作一致性问题(如插入/删除无需判空)。- B+树的分页解决数据分布与查询效率问题。
注:学习MySQL时的笔记,笔记来源是尚硅谷的课件
-
学习的视频:123-页结构之最小最大记录行格式之记录头信息
-
课件整理:deepseek

浙公网安备 33010602011771号