data_free仅反映物理空洞,逻辑碎片需结合页利用率和b+树深度判断;碎片率>25%或data_free>100mb且表较小时才需干预。

不能只看 Data_free,它只是物理空洞的粗略信号,真正影响查询性能的逻辑碎片(比如页内利用率低、B+ 树深度异常)完全不体现在这个字段里。
查 Data_free 与总大小比值,但必须结合表规模判断
直接执行:SELECT table_name, data_length, index_length, data_free, ROUND(100 * data_free / NULLIF(data_length + index_length, 0), 2) AS frag_pct FROM information_schema.tables WHERE table_schema = 'your_db' AND table_name = 'your_table';
注意三点:
-
Data_free是字节单位,小表(比如data_length只有 5MB)出现 50MBData_free很可能是误报(InnoDB 最小分配单位是 extent,1MB) - 碎片率
frag_pct > 25%才算真值得干预;data_free > 100 * 1024 * 1024(即 100MB)且表本身不大(data_length ),说明页分裂已很严重 - 如果
innodb_file_per_table = OFF,Data_free值对ibdata1无效——此时查出来再大也没法通过单表操作释放空间
看 INFORMATION_SCHEMA.INNODB_SYS_INDEXES 中页利用率
这是更贴近真实索引结构的指标。运行:SELECT index_name, n_fields, page_count, n_leaf_pages, ROUND(n_leaf_pages / NULLIF(page_count, 0), 3) AS leaf_ratio FROM information_schema.INNODB_SYS_INDEXES WHERE table_name = 'your_table' AND database_name = 'your_db';leaf_ratio 低于 0.65 就说明叶子节点页大量空洞,B+ 树效率已受损。
-
page_count是该索引总共分配的页数,n_leaf_pages是实际存数据的叶子页数 - 二级索引的
leaf_ratio比聚簇索引更容易偏低——尤其当更新集中在非主键字段时 - 不要忽略
n_fields:如果一个复合索引定义了 5 列但只用前 2 列做查询条件,leaf_ratio再高也意义有限
用 EXPLAIN 和 SHOW INDEX 验证逻辑碎片影响
物理空洞不等于慢查询,得看实际执行是否被拖累:
- 对高频查询加
EXPLAIN,观察rows和key_len是否稳定;如果同样条件多次执行rows波动剧烈(比如从 1000 跳到 5000),大概率是统计信息陈旧 + 索引页空洞导致采样失真 -
SHOW INDEX FROM your_table中Cardinality如果长期远低于实际行数(比如表有 100 万行但Cardinality只有 8 万),不是索引没建好,而是碎片让优化器无法准确估算选择性 - 执行
ANALYZE TABLE your_table后Cardinality无明显变化,基本可确认是页内碎片而非统计滞后问题
别漏掉缓冲池层面的间接证据
碎片最终会反映在内存和 I/O 行为上:
- 监控
Innodb_buffer_pool_read_requests和Innodb_buffer_pool_reads的比值,持续低于 99%(即每 100 次请求有超过 1 次磁盘读)且innodb_page_size没调大,说明碎片抬高了物理 I/O - 对比相同查询在 OPTIMIZE 前后的
Handler_read_next和Handler_read_rnd_next:后者大幅下降,说明范围扫描路径变短了 - 如果表用了
ROW_FORMAT=COMPRESSED,Data_free高可能只是压缩块未重排,不代表逻辑碎片——这种场景优先看leaf_ratio和查询延迟
真正麻烦的是那种 Data_free 不高但 leaf_ratio 低迷、EXPLAIN 显示 key_len 异常偏小的组合——它说明索引结构本身已失衡,但所有表面指标都“看起来还行”。这种碎片最难被发现,也最需要结合业务写入模式去反推。











