ANALYZE TABLE是MySQL官方唯一支持的修正Cardinality偏差的标准手段,通过重新采样更新统计信息,但受采样页数、数据倾斜等影响无法保证100%准确。

Cardinality严重偏离时,ANALYZE TABLE是唯一标准手段
它不能保证100%修好,但这是MySQL官方唯一支持的、能触发重新采样并更新统计信息的操作。Cardinality本质是估算值,不是精确计数——InnoDB默认只采样innodb_stats_sample_pages个页(5.7默认20,8.0+默认20),样本少 + 数据倾斜 + 大量重复值,就会让CARDINALITY比真实COUNT(DISTINCT col)低几个数量级。比如你查SHOW INDEX FROM user_log看到CARDINALITY是12,但实际SELECT COUNT(DISTINCT user_id) FROM user_log返回240万,这就必须干预。
什么情况下必须手动执行ANALYZE TABLE
别一慢就跑。真正该动手的信号很具体:
-
EXPLAIN里rows预估和实际返回行数差一个数量级以上(比如预估300,实际扫了28万行) -
SHOW INDEX中某列CARDINALITY长期为0或1,但你知道该列几乎每行都不同 - 刚执行完大批量
INSERT、DELETE或OPTIMIZE TABLE,但EXPLAIN没变 - 优化器明明有索引却选
type: ALL,且key字段为空或不是你期望的那个
执行ANALYZE TABLE前必须确认的三件事
否则大概率白忙一场:
- 确认表引擎是
InnoDB——ANALYZE TABLE对MyISAM有效,但机制不同;MyISAM要用myisamchk -a - 检查
innodb_stats_persistent是否为ON(8.0+默认开,5.7默认关):关的话,重启后统计清空,ANALYZE结果不落盘 - 排除干扰项:
WHERE DATE(created_at) = '2024-01-01'这类函数用法直接让索引失效;user_id = '123'(字符串)vsuser_id是INT,会触发隐式转换,优化器不敢走索引
执行时容易踩的坑和应对方式
ANALYZE TABLE本身不阻塞DML,但会加表级读锁——意味着期间不能DROP TABLE、ALTER TABLE,也不能并发再跑一次ANALYZE TABLE。大表务必避开高峰期。
- 想提高精度?临时调大采样页数:
SET GLOBAL innodb_stats_persistent_sample_pages = 100,再执行ANALYZE TABLE t_user - 不想写binlog?用
ANALYZE LOCAL TABLE t_user(注意:仅影响当前会话的binlog记录) - 执行完发现
EXPLAIN没换索引?先查information_schema.STATISTICS确认CARDINALITY真变了;再看有没有FORCE INDEX硬编码覆盖;最后检查是否存在更优的复合索引(比如(a,b)比单列a覆盖更好)
统计不准这事从不报错,安静地拖垮性能。真正管用的做法,是把它变成低峰期自动巡检动作,而不是等慢得扛不住才想起来跑一次。


















