不是索引“失效”,而是统计信息过期与B+树空洞堆积导致优化器误选执行计划;需查SHOW INDEX的Cardinality及INNODB_INDEX_STATS的n_diff_pfx01、n_leaf_pages,执行ANALYZE TABLE或重建索引修复。

不是索引“失效”了,而是统计信息过期 + B+ 树空洞堆积,让优化器选错了执行计划。
为什么删完数据查询变慢?本质是优化器“看走眼”了
DELETE 不会删掉索引结构,但会留下大量空洞页(empty pages),同时 InnoDB 的统计信息(如 Cardinality)仍基于旧采样,没自动更新。结果就是:EXPLAIN 显示 rows 高估几倍甚至几十倍,优化器误判成本,放弃本该走的索引,退化成全表扫描。
常见现象包括:
- 明明
WHERE status = 'done'有索引,EXPLAIN 却显示type: ALL -
key_len突然变小,或key列为空 - 同一语句,删前毫秒级,删后秒级,且
ANALYZE TABLE后立刻恢复
怎么确认是不是统计失真导致的?
别猜,直接查系统视图:
- 运行
SHOW INDEX FROM your_table,重点看Cardinality列 —— 如果远低于该列实际唯一值数量(比如字段有 10 万不同值,Cardinality却是 1 或 100),基本就是统计过期 - 查
information_schema.INNODB_INDEX_STATS,对比n_diff_pfx01(估算唯一值)和n_leaf_pages(叶子页数):若后者异常高、前者异常低,说明空洞多、分布碎 - 执行
ANALYZE TABLE your_table后再查一遍,如果Cardinality无明显变化,说明默认采样率不够,需强制更新
重建索引 ≠ 万能解,得看场景选对方式
重建目标是清理空洞 + 刷新统计,但不同命令行为差异大:
-
OPTIMIZE TABLE your_table:InnoDB 下等价于ALTER TABLE ... FORCE+ 更新统计;8.0+ 支持在线,但 DML 仍会被短暂阻塞;适合中小表( -
ALTER TABLE your_table ENGINE=InnoDB或FORCE:显式重建聚簇索引,重排物理顺序;需要约等于原表大小的额外磁盘空间 -
DROP INDEX idx_name ON your_table; ADD INDEX idx_name (col);:只重建单个二级索引,不碰聚簇索引;适合某索引严重退化但主键结构尚可时;注意 DROP 瞬间该列就无法走索引
别在高峰期跑 OPTIMIZE 或 ALTER TABLE,尤其大表 —— 它们会触发全表拷贝和锁。
真正要控制的,是删除动作本身
频繁重建索引只是补救,源头问题在 DELETE 方式:
- 删 >30% 数据时,优先用建表替换:
CREATE TABLE new_t AS SELECT * FROM old_t WHERE keep_condition;+RENAME,比 DELETE + OPTIMIZE 更快更稳 - 必须用 DELETE 时,严格按主键/时间范围分批:
DELETE FROM t WHERE id BETWEEN 1000001 AND 1010000 LIMIT 10000,避免LIMIT ... OFFSET和无序扫描 - 删完立即
ANALYZE TABLE,别等第二天——统计滞后是查询变慢最常被忽略的临门一脚
空洞和统计偏差不会自己修复,也不会在你下次 SELECT 时悄悄更新。它就在那里,等着某条看似正常的查询突然卡住。

















