先确认是统计信息过期而非sql或索引问题:explain中rows预估与实际扫描量差一个数量级以上,且show index的cardinality远低于count(distinct列),再排除函数使用、隐式转换及innodb_stats_persistent=off等干扰。

直接执行 ANALYZE TABLE 能解决大部分因统计失真导致的慢查,但不是所有“一慢就跑”都能见效——它只对基数(CARDINALITY)严重偏离真实值的情况有效,且必须配合正确的配置和验证步骤。
怎么确认真是统计信息错了,而不是SQL或索引问题?
别跳过这步,否则白忙活。真正该动手的信号很具体:
-
EXPLAIN中rows预估和实际扫描行数差一个数量级以上(比如预估 200,SELECT COUNT(*)实际返回 18 万) -
SHOW INDEX FROM table_name里某列的CARDINALITY是 0 或 1,但你知道该列几乎每行都不同(例如主键、订单号、用户ID) -
SELECT COUNT(DISTINCT col) FROM table_name返回几十万,而information_schema.STATISTICS.CARDINALITY显示才几百 - 排除干扰项:
WHERE DATE(created_at) = '2024-01-01'这类函数用法会让索引失效;user_id = '123'(字符串)vsuser_id是INT,会触发隐式转换
ANALYZE TABLE 执行前必须检查的三件事
缺一不可,否则大概率执行完没变化:
- 确认表引擎是
InnoDB——ANALYZE TABLE对MyISAM也生效,但机制不同;MyISAM得用myisamchk -a - 查
SHOW VARIABLES LIKE 'innodb_stats_persistent',必须为ON(MySQL 5.7 默认关,8.0+ 默认开;升级后配置文件可能残留OFF) - 顺手关掉
innodb_stats_on_metadata:设为OFF,否则SHOW TABLE STATUS会偷偷触发临时分析,拖慢元数据查询
大表/倾斜数据怎么让 ANALYZE 更准?
默认采样页数太低,尤其对千万行以上或分布极端不均的列(如 95% 值为 1 的状态字段),必须干预:
- 小表(百万行以内):
ANALYZE TABLE your_table直接跑,通常秒级完成 - 大表(千万行以上):先调高采样精度:
SET GLOBAL innodb_stats_persistent_sample_pages = 128,再执行ANALYZE TABLE your_table - 极端倾斜列(如
status字段):强制全量扫描:ANALYZE TABLE your_table WITH SAMPLE 100 PERCENT - 分区表:不指定分区默认只刷元数据,得写成
ANALYZE TABLE t PARTITION(p2024)
执行后为什么 EXPLAIN 没变?常见卡点在哪?
命令返回 OK 不代表真生效,必须验证:
- 查
information_schema.STATISTICS确认CARDINALITY值是否更新,不能只看时间戳 - 检查有没有
FORCE INDEX或USE INDEX硬编码覆盖,这类提示会完全绕过优化器决策 - MySQL 8.0.22+ 需要手动刷新优化器成本缓存:
FLUSH OPTIMIZER_COSTS,否则可能仍用旧计划 - 高峰期执行会加表级读锁,阻塞
DROP TABLE、ALTER TABLE,甚至并发另一个ANALYZE TABLE也会等锁——务必避开业务高峰
统计不准这事从不报错,安静地拖垮性能。最危险的是你以为索引还在工作,其实优化器早已悄悄弃用。真正管用的做法,是把它变成低峰期自动巡检动作,并监控 CARDINALITY 波动——偏差超过 30%,就自动触发重采样。











