判断 mysql 索引碎片主要看 information_schema.table S 中 DATA_FREE 字段,结合 data_length+index_length 计算碎片率;碎片率>20% 或 DATA_FREE>100MB 建议整理,常用 OPTIMIZE TABLE 或 ALTER TABLE … ENGINE=InnoDB 重建表,大表宜用 pt-online-schema-change 在线处理。

mysql 索引 碎片主要源于频繁的增删改操作,尤其在 InnoDB 中,delete只是逻辑标记、UPDATE 可能引发页分裂、INSERT 随机主键导致页填充率低——这些都会让数据页变得稀疏,形成“空洞”,最终影响查询性能和磁盘空间利用率。处理的关键不是盲目优化,而是先识别、再按需整理,并兼顾后续预防。
怎么判断表有没有索引碎片
核心看 DATA_FREE 字段,它代表表中未被使用的 字节 数(即物理空洞大小)。结合数据 + 索引总大小,可算出碎片率:
- 执行查询:
select table_name, data_length, index_length, data_free,
ROUND((data_free / NULLIF(data_length + index_length, 0)) * 100, 2) AS frag_pct
FROM information_schema.tables
WHERE table_schema = ‘your_db’ AND data_free > 0; - 一般认为碎片率 > 20% 就值得整理;超过 50% 或 DATA_FREE > 100MB 建议优先处理
- InnoDB 表注意:即使
data_free = 0,也不绝对代表无碎片(比如页内碎片无法通过该字段体现),但这是最实用的初筛指标
常用整理方法及适用场景
真正生效的整理本质都是 重建表结构 , 区别 在于语法 封装 和锁行为:
- OPTIMIZE TABLE table_name
对 MyISAM 是原生命令;对 InnoDB 实际等价于ALTER TABLE …… ENGINE=InnoDB,会触发重建 +ANALYZE。期间加 读锁(可查不可写),适合低峰期单表操作 - ALTER TABLE table_name ENGINE=InnoDB
更显式,兼容性好,效果同上。如果表原本就是 InnoDB,执行它仍会重建并整理碎片 - ALTER TABLE table_name FORCE
轻量级重建语法,不改变引擎,仅强制重写数据文件,同样能清理碎片,开销略小 - 大批量操作时,用 mysqlcheck -o database_name 可批量优化整个库的表
哪些情况要特别注意
不是所有碎片都必须立刻处理,得结合业务权衡:
- 高写入低查询的表(如日志表):碎片影响小,但频繁 OPTIMIZE 反而加重 I / O 压力,建议按月或按分区归档后清理
- 大表(>10GB):重建耗时长、锁表久,生产环境慎用。可考虑 Percona Toolkit 的
pt-online-schema-change在线重建,避免服务中断 - 使用 UUID 或随机 字符串 作主键 :这是碎片“加速器”,长期来看应优先 重构 为自增 ID 或时间有序 ID,比定期整理更治本
- innodb_file_per_table = OFF 时,碎片无法通过单表操作释放到磁盘——必须迁移至独立表空间后才有效
日常怎么减少碎片产生
预防比修复更重要: