查主键碎片不能只看data_free,因其反映全表空间空闲总量,而非主键页填充率;真正指标是index_length/(n_leaf_pages×16384)

查主键碎片不能只看 Data_free
很多人一看到 SHOW TABLE STATUS 里 Data_free 很大,就断定是主键索引碎了,直接 OPTIMIZE TABLE。但这是错的——Data_free 是整个表空间(含聚簇索引+所有二级索引)的未使用空间总和,它高 ≠ 主键页利用率低。真正反映主键碎片程度的是叶子页填充率:n_leaf_pages × 16384(每页 16KB)对比 Index_length。如果 Index_length / (n_leaf_pages × 16384) ,才说明主键 B+ 树内部空洞严重。
ALTER TABLE ... REBUILD 比 OPTIMIZE TABLE 更适合主键重建
MySQL 8.0.23+ 中,ALTER TABLE t REBUILD 是重建聚簇索引(即主键)最干净的方式:
- 只重排数据页和索引页,不重置统计信息 → 避免执行计划突变(
OPTIMIZE TABLE会隐式触发ANALYZE TABLE,可能让原本走idx_created_at的查询突然全表扫) - 默认走
ALGORITHM=INPLACE,不生成临时表,磁盘空间压力小 - 不锁表(
LOCK=DEFAULT下仍允许并发 DML),比OPTIMIZE TABLE在高峰期更可控
低版本(ALTER TABLE t ENGINE=InnoDB ALGORITHM=INPLACE LOCK=NONE,效果等价但语义更明确。
主键碎片常被误判为“慢查询”,其实是缓冲池没热
执行完重建后第一条查询仍然慢?大概率不是碎片没清干净,而是:
-
innodb_buffer_pool_size设置过小,新加载的数据页很快被淘汰,后续查询反复磁盘读 - 重建后首次访问触发冷启动,
innodb_buffer_pool_reads短时飙升属正常现象 - 监控要看连续 5–10 分钟的
Buffer pool hit rate是否稳定在 99%+,而不是单次SELECT延迟
别急着再跑一遍重建——先确认缓冲池是否已充分预热。
真正拖慢主键扫描的,往往是 TEXT/BLOB 字段回表
即使主键页填充率达 95%,只要表里有大字段且查询用了 SELECT * 或未覆盖索引,InnoDB 就必须回聚簇索引取整行。这时碎片影响会被放大:一页本可存 100 行,因空洞只剩 60 行,同样范围扫描要多读 67% 的页。
解决方法很实在:
- 把
TEXT/BLOB字段拆到附表,主表只留 ID 关联 - 查询时明确列出需要的列,杜绝
SELECT * - 对高频查询字段建覆盖索引,避免回表
主键碎片是症状,不是病根;回表 + 大字段 + 缓冲池小,三者叠加才是慢查询的常见真实组合。











