索引碎片率超20%且EXPLAIN显示rows远大于filtered*rows或频繁Using temporary/Using filesort才需优化;否则可能无效甚至更慢。

索引碎片率超过 20% 且 EXPLAIN 显示 rows 异常偏高或频繁出现 Using temporary/Using filesort,才值得动手;否则优化可能白忙,甚至让查询更慢。
怎么确认是索引碎片拖慢了查询?
别只盯着 Data_free 高或磁盘满了——那只是表象。真正要盯的是它是否影响了扫描效率:
-
Data_free / (Data_length + Index_length) > 0.2(即碎片率超 20%)是硬门槛,低于这个值基本不用理 - 查
EXPLAIN结果里rows是否远大于filtered * rows,比如rows=10000但filtered=5,说明 B+ 树页内空洞多、实际扫描行数虚高 - 慢查询日志中反复出现
Using temporary; Using filesort,且没走覆盖索引,大概率是碎片导致临时表膨胀、排序缓存命中率下降 - 用
SELECT (data_length + index_length) / table_rows AS avg_row_size算平均行大小,如果比业务字段总和大 2 倍以上(比如字段加起来 200 字节,却算出 600 字节),说明页填充率极差
InnoDB 表重建索引该用 OPTIMIZE TABLE 还是 ALTER TABLE ENGINE=InnoDB?
ALTER TABLE t ENGINE=InnoDB 更可控,尤其适合生产环境:
- 语义明确,行为稳定;MySQL 5.6+ 支持显式控制锁级别:
ALTER TABLE t ENGINE=InnoDB ALGORITHM=INPLACE LOCK=NONE - 不会自动触发
ANALYZE TABLE,避免统计信息突变导致执行计划抖动 - 不重置
AUTO_INCREMENT值,也不影响外键或触发器定义 - 效果完全等同于
OPTIMIZE TABLE(InnoDB 下本质就是隐式ALTER TABLE ENGINE=InnoDB),但更轻量
执行前必须检查的三件事,缺一不可
否则会卡住、失败或白跑:
- 磁盘剩余空间 ≥
Data_length + Index_length(从SHOW TABLE STATUS LIKE 't'查),不是“差不多”,是必须够——重建过程会先写新.ibd文件,再原子替换 - 确认无长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE trx_started,有结果就别动,等它结束 - 检查
innodb_file_per_table必须为ON;若为OFF,所有表共用ibdata1,重建无法把空间还给操作系统,只会内部复用
phpEnv 环境下 MyISAM 表的 OPTIMIZE TABLE 容易卡住?
phpEnv 的 MySQL 配置(通常在 D:\phpEnv\phpEnv\MySQL\my.ini)里常含 myisam_recover_options = BACKUP,FORCE 或类似项,这会让 OPTIMIZE TABLE 在启动时自动触发修复,反而阻塞操作:
- 执行前先临时注释掉配置中的
myisam_recover相关行 - 再运行
OPTIMIZE TABLE your_table_name,成功后看到Msg_text: OK - 重启 MySQL 前务必把配置改回去,否则下次启动可能卡在 “repairing” 状态
重建完首条查询必然慢——这是正常现象,因为数据页刚加载进 buffer pool,后续查询才会逐步回暖。真正容易被忽略的是:优化后没跑 ANALYZE TABLE 更新统计信息,优化器还在用旧的 cardinality 做决策,执行计划可能根本没变。


















