mysql升级后explain显示全表扫描但索引存在,实为优化器统计信息过时导致误判;需执行analyze table更新统计,并检查innodb_stats_persistent等配置确保长期准确。

升级后 EXPLAIN 显示全表扫描,但索引明明存在
这是 MySQL 升级后最典型的“假失效”现象:不是 SQL 写错了,也不是索引没了,而是优化器拿到的统计信息过时或不准,导致它误判“走索引不如扫全表”。MySQL 5.6+ 尤其依赖 innodb_stats_persistent 和实时采样,而旧版本(如 5.7)可能长期没跑过 ANALYZE TABLE,升级到 8.0 后优化器更“较真”,立马暴露问题。
- 先确认是否真失效:
SHOW INDEX FROM table_name看Cardinality值是否明显偏低(比如远小于实际去重数),或对比information_schema.TABLES.TABLE_ROWS和真实行数偏差超 20% - 对单表快速修复:
ANALYZE TABLE your_table;—— 注意:大表会锁读,建议在低峰执行 - 永久避免:设
innodb_stats_persistent = ON(MySQL 5.6+ 默认开启,但升级后需检查是否被配置覆盖),并调大采样页数:innodb_stats_persistent_sample_pages = 128(默认 20,小值易导致估算失真) - 别信
SHOW TABLE STATUS的Rows字段——它只是估算,且不随ANALYZE实时更新;真要看统计质量,查information_schema.STATISTICS中的Cardinality
slow_query_log 开了却没记录,或日志里全是“未走索引”
升级后默认行为可能变化:MySQL 8.0 默认关闭 log_queries_not_using_indexes,且 long_query_time 在会话级设置不继承全局值,容易造成“以为开了其实没生效”的错觉。
- 检查是否真开启:
SHOW VARIABLES LIKE 'slow_query_log';和SHOW VARIABLES LIKE 'log_queries_not_using_indexes';必须都为ON -
long_query_time是浮点数,设SET GLOBAL long_query_time = 1;后,当前连接不会生效,必须断开重连或显式SET SESSION long_query_time = 1; - 路径权限常被忽略:MySQL 进程用户(如
mysql)必须对slow_query_log_file所在目录有写权限,否则静默失败——查错误日志:tail -f /var/log/mysql/error.log,找Failed to open log file类报错 - 8.0+ 不再支持查询缓存,
query_cache_type = OFF是必须项;若旧配置残留ON,会导致部分语句被跳过慢日志记录
ORDER BY 突然变慢,EXPLAIN 显示 Using filesort 但字段有索引
这不是索引失效,是排序策略变更。MySQL 8.0.20+ 废弃了 max_length_for_sort_data,改用更激进的“行ID排序”(rowid sort),当查询字段多、结果集大时,I/O 暴增,哪怕有索引也扛不住。
- 验证是否此问题:执行
EXPLAIN FORMAT=JSON,看输出中"sort_mode": "<sort_key additional_fields>"</sort_key>是否缺失,若只有"<sort_key rowid>"</sort_key>,说明走了 rowid 排序 - 临时缓解:加覆盖索引,让
SELECT字段全部包含在索引中,避免回表触发 rowid 排序,例如INDEX (status, created_at, id, user_id)配合WHERE status='PENDING' ORDER BY created_at - 别碰
optimizer_switch关闭排序优化——8.0+ 的排序逻辑已重构,关掉反而更差;重点应放在减少SELECT *和控制LIMIT数量
生产环境不敢跑 ANALYZE TABLE,又怕统计持续恶化
大表 ANALYZE 可能卡住 DML,但放任不管,优化器会越来越“瞎”。折中方案不是不分析,而是控制粒度和时机。
- 用采样代替全量:
ANALYZE TABLE your_table PERSISTENT FOR ALL;(MySQL 8.0+ 支持指定采样率)或手动降低采样页:SET SESSION innodb_stats_transient_sample_pages = 16;再执行 - 只分析高频慢查涉及的列:
ANALYZE TABLE your_table UPDATE HISTOGRAM ON create_time, status;(8.0+ 直方图功能,比传统统计更准,且不锁表) - 监控驱动:定期查
SELECT TABLE_NAME, CARDINALITY FROM information_schema.STATISTICS WHERE TABLE_SCHEMA='your_db' AND CARDINALITY/TABLE_ROWS ,只对区分度骤降的索引针对性处理
统计信息不是一劳永逸的开关,它是优化器的“视力矫正镜”——升级后度数变了,得重新验光配镜,而不是捂着眼睛硬撑。











