bigywater.tech
MySQL / InnoDB · 73

B+Tree 页合并

大量删除数据之后立刻检查页合并计数器,再用一条完全不命中数据的DELETE去'路过'同一片索引区域,看合并到底是什么时候真实被触发的。

删除后=0
大量删除后页合并计数器真实的即时反应
路过后=146
后续一条无关查询真实顺带触发的合并尝试

做的事:先大量删除,再看页合并计数器动不动

代码SET GLOBAL innodb_monitor_enable='index_page_merge_successful'; SET GLOBAL innodb_monitor_enable='index_page_merge_attempts'; DELETE FROM seq_pk WHERE id % 10 < 7; -- 真实删掉约70%的行

验证:删除刚发生完,页合并计数器真实纹丝不动

实测 · 本机执行输出
删除70%的行之后:
  index_page_merge_attempts:    0
  index_page_merge_successful:  0   ← 真实都还是0

本以为删掉这么大比例的数据,会真实立刻触发一批相邻页面因为都变得很空而合并,结果两个计数器真实都停在0。这和上一篇《簇索引页分裂》里"插入立刻触发分裂"的即时性完全不对称——分裂是插入路径上真实必须马上处理的(页面写不下新数据了,不能拖),但合并没有这种紧迫性,一个变空的页面哪怕暂时保留着不合并,也不影响数据的正确性,只是浪费了一些空间。

验证:一条完全无关、不匹配任何数据的DELETE,真实顺带触发了合并

代码-- 这条DELETE的条件范围内,真实没有任何一行数据存在 DELETE FROM seq_pk WHERE id BETWEEN 300000 AND 600000;
实测 · 本机执行输出
这条0行命中的DELETE执行之后:
  index_page_merge_attempts:    146   ← 真实凭空多了146次
  index_page_merge_successful:   10   ← 真实有10次真的合并成功了
这条DELETE语句真实没有删除任何一行数据(id范围内本来就没有数据),但它需要真实定位到这个范围在B+树里"应该在哪里",这个过程真实会遍历经过此前那次大规模删除留下的、已经变得很空的页面——InnoDB会顺带评估这些经过的页面是不是可以和相邻页面合并,真实是在这次遍历里触发了146次合并尝试,其中10次真的成功了。这印证了一个真实的机制:B+树的页合并不是被"删除操作"本身同步驱动的,而是"任何真实需要遍历经过那部分树结构的后续操作"都可能顺带触发合并检查——一张表如果删除之后长期没有其他操作再去访问那部分索引,那些空页面就可能真实长期留在那里不被回收,这也是OPTIMIZE TABLE(下一篇)存在的意义:不依赖"顺带触发",直接强制重建整个索引结构。

分步演示:从"删除后没反应"到"路过才触发"

每一步都是本文上面用真实计数器跑出来的原始结果。

页合并真实是被后续遍历顺带触发的
这一步新验证的操作

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),延续上一篇《簇索引页分裂》的表
    • information_schema.INNODB_METRICS里的index_page_merge_attempts/index_page_merge_successful
    京ICP备2026067502号-1京公网安备11010502063487号