EXPLAIN的rows和key不准是因为优化器依赖过时或失真的统计信息估算;基数低估、数据倾斜、隐式转换、函数索引失效等导致估算严重偏离实际,需用ANALYZE TABLE更新统计并结合EXPLAIN ANALYZE验证。

执行计划变,不是优化器“抽风”,而是它基于统计信息重新算了一笔账——账本(统计信息)错了,账自然就平不了。
EXPLAIN 的 rows 和 key 为什么突然不准了
MySQL 优化器不扫描全表,也不实时数行数,它靠采样估算:比如某索引的 CARDINALITY(基数)、值分布、页数量。这些数据缓存在内存或落盘(取决于 innodb_stats_persistent)。一旦表被批量写入、清空重建、或长时间未更新,旧估算就和真实数据脱节。
-
rows显示 800,实际扫了 200 万行?大概率是基数严重低估,优化器以为索引很“好用”,结果选错路 -
key是NULL,但明明有索引?说明优化器算出来“走这个索引成本比全表还高”,根源常是CARDINALITY接近 1 或 0(比如 status 字段 99% 是 'active') - 测试环境 OK,上线就慢?因为生产数据量/倾斜度不同,而统计信息没同步更新
ANALYZE TABLE 不是万能解药,但不跑它问题只会更隐蔽
它不是刷新缓存,而是重新读取聚簇索引页、重新采样、重算基数。但默认只采 innodb_stats_persistent_sample_pages(默认 20 页),对大表或倾斜数据极不友好。
- 执行前先查倾斜度:
SELECT COUNT(DISTINCT col) / COUNT(*) FROM t,若 - 调高采样页再刷:
SET GLOBAL innodb_stats_persistent_sample_pages = 200,然后ANALYZE TABLE t - 分区表必须显式指定:
ANALYZE TABLE t PARTITION(p2024),否则只分析元数据,不碰分区数据页 - 如果
innodb_stats_persistent = OFF(5.7 以前默认关),ANALYZE TABLE只改内存,重启即失效
为什么 ANALYZE TABLE 跑完,EXPLAIN 还是没换索引
新统计信息可能根本没被优化器“看见”,或者被更高优先级规则覆盖了。
- 查
information_schema.STATISTICS表,确认CARDINALITY值真变了,不只是STATS_INITIALIZED时间戳更新 - SQL 里有没有
FORCE INDEX或USE INDEX?它们会完全绕过优化器决策 - 是否存在更优的覆盖索引?比如你期待
idx_a,但优化器发现idx_a_b能避免回表,就直接选后者——这不是 bug,是成本模型算出来的更优解 -
ANALYZE TABLE不修复索引结构缺陷,也不解决谓词本身选择性差的问题(如WHERE b > ?返回 90% 行,再准的统计也救不了)
最麻烦的不是报错,是它安静地错着:你看到 key 有值,就以为索引在干活,其实优化器早因基数崩塌悄悄弃用了它。定期验证 EXPLAIN rows 和实际扫描行数的偏差,比等告警更靠谱。


















