先看EXPLAIN中type是否为ALL、key是否为NULL,确认索引存在且类型匹配;若rows预估与实际扫描量相差十倍以上,再查information_schema.STATISTICS中Cardinality与COUNT(DISTINCT)差异,即可判定是统计信息过期而非索引失效或SQL错误。

怎么确认是统计信息过期,而不是索引失效或SQL写错?
别急着删索引或改SQL——先看EXPLAIN里type是不是ALL,key是不是NULL,同时确认该列确实建了索引、类型匹配(比如ParentPayId是BIGINT,IN列表里没混字符串)。如果这些都对,但rows预估和实际扫描量差十倍以上(比如预估100行,实际扫50万行),就大概率是统计信息拖了后腿。
验证方法:查information_schema.STATISTICS里的Cardinality,再和COUNT(DISTINCT your_col)对比。比如状态字段只有3个值,Cardinality却显示1200,说明采样严重失真。
注意:SHOW INDEX或SHOW TABLE STATUS里的Cardinality和Rows字段不可信——它们是粗粒度估算,尤其大表偏差常超50%。
ANALYZE TABLE 什么时候能用,什么时候要绕开?
ANALYZE TABLE是最直接的修复动作,但它不是“点了就完事”的按钮:
- 小表(百万行以内):直接执行
ANALYZE TABLE your_table,通常秒级完成 - 大表(千万行以上):默认采样页数
innodb_stats_persistent_sample_pages = 20太低,容易继续不准;先调高:SET GLOBAL innodb_stats_persistent_sample_pages = 128 - 分布极端不均的列(如95%值为
1的状态字段):必须强制全量扫描:ANALYZE TABLE your_table WITH SAMPLE 100 PERCENT - 高峰期严禁执行:它会加只读锁,阻塞
SELECT,错误日志里会出现Waiting for table flush
为什么刚跑完 ANALYZE TABLE,过两天又慢了?
自动更新机制在生产环境基本形同虚设:
- InnoDB 默认触发条件是“累计修改行数 ≥ 当前表行数 × 10%”,一张5000万行的表得改满500万行才更新——业务早扛不住
- 必须确保
innodb_stats_persistent = ON(MySQL 5.6+默认开,但升级后配置文件可能残留OFF) - 顺手关掉
innodb_stats_on_metadata = OFF(默认ON),否则SHOW TABLE STATUS会偷偷触发临时分析,拖慢元数据查询 - MySQL 8.0+ 默认关闭
log_queries_not_using_indexes,你以为慢日志开着,其实连“未走索引”的语句都不记——检查并设为ON
升级MySQL后查询突然变慢,八成是这个坑
从5.7升到8.0+后,优化器更“较真”,旧统计立刻暴露:
- 升级后第一件事:查
SHOW VARIABLES LIKE 'innodb_stats_persistent',不是“应该开了”,是必须为ON -
SHOW INDEX里Cardinality明显偏低?别犹豫,立刻ANALYZE TABLE - 发现慢查询日志没记录?不是磁盘满了,而是
log_queries_not_using_indexes被默认关了 -
long_query_time设了全局值,但当前连接不生效——必须断开重连,或显式SET SESSION long_query_time = 1
统计信息不是“修一次就一劳永逸”的东西,它像轮胎气压——需要定期校准,尤其在数据分布突变(比如新上线一个状态值)、批量导入、或版本升级之后。忽略它,等于让优化器蒙着眼开车。


















