MySQL无直接索引碎片率指标,可通过SHOW TABLE STATUS中Data_free与Data_length占比估算:若Data_free>0且占(Data_length+Data_free)超20%,则存在明显碎片。

怎么查 MySQL 表索引的碎片率
MySQL 本身不直接暴露“索引碎片率”这个数值,但可以通过 INFORMATION_SCHEMA.INNODB_SYS_INDEXES 和 INFORMATION_SCHEMA.INNODB_SYS_TABLES 结合估算——核心是看 DATA_FREE 占比和页利用率。
最实用的判断方式是查 SHOW TABLE STATUS 输出里的 Data_free 和 Data_length:
SHOW TABLE STATUS LIKE 'orders';
如果 Data_free 明显大于 0(比如 > 50MB),且占 Data_length + Data_free 比例超过 20%,基本说明有明显碎片。InnoDB 的“空闲空间”不是完全闲置,而是已分配但未被有效利用的页,常由频繁 DELETE / UPDATE 引起。
-
Data_free是 InnoDB 表空间中未被使用的字节数,仅对innodb_file_per_table = ON且独立表空间(.ibd 文件)有效 - MyISAM 表用
Data_free意义不同,它表示删除后留下的空隙,可被新行复用,但不触发自动整理 - 不要只看绝对值:100MB 的
Data_free对 1GB 表不算严重,对 10MB 表就值得干预
OPTIMIZE TABLE 到底做了什么
OPTIMIZE TABLE 在 InnoDB 中本质是重建表:创建新临时表 → 按主键顺序拷贝数据 → 替换原表 → 删除旧表文件。它会重排聚簇索引、合并页、释放 Data_free 空间,并更新统计信息。
但它不是万能的:
- 执行期间会加
SX(共享读写)锁,DML 操作会被阻塞(尤其大表,可能卡住业务) - 需要磁盘空间 ≈ 原表大小(临时表 + 原表同时存在),若磁盘快满,操作会失败并残留临时文件
- MySQL 5.7+ 默认开启
innodb_defragment,但该参数仅影响后台页整理,不改变Data_free,也不能替代OPTIMIZE TABLE - 对
TEXT/BLOB列较多的表,OPTIMIZE TABLE可能不会显著降低Data_free,因为这些列数据存于溢出页,不随主键排序重排
重建表更可控的替代方案
想绕过 OPTIMIZE TABLE 的锁和空间风险,可用 ALTER TABLE ... ENGINE=InnoDB 或 ALGORITHM=INPLACE(MySQL 5.6+)手动重建:
ALTER TABLE orders ENGINE=InnoDB, ALGORITHM=INPLACE, LOCK=NONE;
但这要求满足严格条件,否则会自动退化为 COPY 模式(等价于 OPTIMIZE TABLE):
-
ALGORITHM=INPLACE仅支持部分 DDL,重建索引、调整 ROW_FORMAT、压缩等可以,但改列类型或加主键不行 -
LOCK=NONE要求表无全文索引、无外键、无虚拟生成列,且 MySQL 版本 ≥ 8.0.29 才对更多场景支持 - 真正零锁重建推荐用
pt-online-schema-change(Percona Toolkit),它通过触发器双写实现,但会增加主从延迟和 binlog 体积
碎片是否真影响性能
碎片主要拖慢的是**全表扫描**和**范围扫描**:页分散导致更多随机 I/O,缓冲池命中率下降。但对于主键等值查询、覆盖索引查询,影响极小——因为只访问少量页,且页内数据连续性不关键。
别一看到 Data_free > 0 就急着优化:
- 写多读少、生命周期短的表(如日志、队列表),碎片可忽略;频繁重建反而增加 I/O 压力
- SSD 上随机 I/O 成本大幅降低,碎片带来的性能衰减比 HDD 小得多
- 如果
SHOW ENGINE INNODB STATUS里Buffer pool hit rate稳定在 99%+,碎片大概率没造成实际瓶颈
真正该优先关注的,是慢查询是否走了预期索引、是否有大量 Using filesort 或 Using temporary ——这些比碎片更直接地指向性能根因。










