ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

页分裂 页合并(索引为什么会慢慢膨胀)

页分裂  页合并(索引为什么会慢慢膨胀)

🔥 一句话总纲

B+Tree 就像图书馆书架:书(行)放满了一格(页) → 再放就得搬书、再开新格 → 结构调整就是页分裂/合并。


1️⃣ B+Tree 页结构回顾

每个页(Page):

  • 默认大小 16KB
  • 叶子节点存索引列 + 行指针(或全部列,聚簇索引)
  • 页满了,无法再插入 → 触发 页分裂

2️⃣ 页分裂(Page Split)

💡 核心理念

“一个页装不下新数据,就拆成两个页,把一半数据搬到新页。”

举例:

页原本:[1,2,3,4,5,6,7,8]
插入 9 → 页满
分裂成:左页:[1,2,3,4]右页:[5,6,7,8,9]
  • 新页要插入到父节点 → 父节点可能也满 → 父节点递归分裂
  • 最坏情况可能上升到根节点 → 树高 +1

🔥 影响

  • 写入慢:拆页需要修改父节点、更新 B+Tree 链表
  • 随机写多 → 脏页多 → Buffer Pool 压力大
  • 页膨胀 = B+Tree 越来越大 → 查询高度增加(I/O 多)

3️⃣ 页分裂触发场景

  1. 顺序插入

    • 自增主键 → 只在最右叶子插入 → 右侧页分裂 → 高性能稳定
  2. 随机插入

    • 主键非自增 → 中间页频繁分裂 → 随机 I/O 多 → 写性能下降
  3. 二级索引

    • 插入任意位置 → 页分裂更频繁

4️⃣ 页合并(Page Merge / Page Compression)

分裂后,有些页可能使用率很低:

  • 删除数据 → 页只剩 1~2 条
  • 页利用率 < 50% → 可合并左/右页 → 减少 B+Tree 膨胀

特点:

  • 异步进行(后台线程)
  • 避免频繁分裂导致树过高、随机 I/O 过多
  • 过度删除 + 插入 → 仍可能反复分裂/合并 → 写入性能波动

5️⃣ 脑补动画版(写入膨胀全过程)

  1. 初始页
[1,2,3,4,5,6,7,8]
  1. 插入 9 → 分裂
左页: [1,2,3,4]   右页: [5,6,7,8,9]
  1. 父节点更新
父页: [4,8] (指向左页、右页)
  1. 随机插入 3.5 → 中间页再次分裂
中间页:[3,4] → [3] [4] 两页  
父页需更新 → 树高度可能增加
  1. 删除大量 5~7 → 页合并
左页: [1,2,3,4] 右页: [8,9]

🎯 结果:树高可能减少,但页空洞仍存在 → 查询仍慢于理想状态


6️⃣ 为什么索引会慢慢膨胀?

  • B+Tree 页分裂是不可避免的,尤其是 随机插入或二级索引
  • 随着数据增长,索引页越来越多 → 树高增加 → 查询深度增加 → I/O 多 → 查询慢
  • 删除数据也不一定立即收缩 → 空洞页存在 → 内存/磁盘占用大
  • 写入频繁 → 分裂/合并频繁 → 写性能下降

7️⃣ 工程师优化策略

问题 解决方式
顺序插入不均衡 尽量用自增主键/预分配 key
二级索引膨胀 控制索引列大小,减少随机插入
页利用率低 定期 OPTIMIZE TABLE 或 rebuild 索引
写入性能下降 批量插入,减少单条随机写

🔥 总结一句话

页分裂和页合并是 B+Tree 自我调整机制,保证查询仍然对 I/O 友好,但随机写、二级索引、大量删除会让树膨胀 → 写慢、查询慢、空间浪费。

返回列表