ANALYZE TABLE是第一反应,因为MySQL 8.0升级后INNODB_TABLESTATS中last_update常为0,优化器无统计信息可用,执行计划必然劣化;索引调整或hint依赖准确统计,此时无效。

升级 MySQL 8.0 后,INNODB_TABLESTATS 中的统计信息不会自动继承或迁移,旧的 last_update 时间戳被清空或重置为 0,优化器“失明”,执行计划立刻劣化——这不是配置没调好,是它根本没数据可参考。
为什么 ANALYZE TABLE 是第一反应,而不是改索引或加 hint
MySQL 8.0 升级后,INNODB_TABLESTATS 表中 last_update 字段常为 0000-00-00 00:00:00 或远早于升级时间,意味着优化器对表行数、索引选择性、字段分布一无所知。此时任何索引调整、JOIN 顺序修改甚至 FORCE INDEX 都可能被忽略或反向生效,因为成本估算完全失真。
- 执行
EXPLAIN ANALYZE会暴露问题:某节点rows=100(估算),但actual rows=85000(真实) -
SELECT * FROM mysql.innodb_table_stats WHERE table_name = 'your_table'返回空或last_update为 0 - 即使有索引,优化器也可能选
type: ALL,只因它“以为”索引过滤后只剩几行,不如全表扫快
ANALYZE TABLE 执行前必须确认的三件事
盲目执行 ANALYZE TABLE 可能无效,尤其在大表或偏斜数据场景下。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 确认是否启用了持久化统计信息:
SELECT @@innodb_stats_persistent必须为ON(8.0 默认开启,但某些迁移方式可能未继承) - 检查采样页数是否过低:
SELECT @@innodb_stats_persistent_sample_pages,默认 20,对亿级表建议临时设为 100:SET GLOBAL innodb_stats_persistent_sample_pages = 100 - 避免在业务高峰执行:该操作会加
MDL_SHARED_READ锁,阻塞 DDL,但不阻塞 DML;若表正被ALTER TABLE占用,会卡住直到 DDL 完成
直方图不是可选项,而是统计信息补丁的关键一环
当字段分布极度偏斜(比如 status 字段 99% 是 'active',仅 1% 是 'archived'),仅靠 ANALYZE TABLE 生成的基数(Cardinality)无法支撑优化器判断,仍会误判 WHERE status = 'archived' 的代价。
- 对高频过滤字段建直方图:
ANALYZE TABLE orders UPDATE HISTOGRAM ON status, created_at - 直方图类型默认为
SINGLE_PREC_HB(等高直方图),8.0.19+ 支持DOUBLE_PREC_HB,精度更高但内存开销翻倍 - 直方图不自动刷新,需手动触发更新;也不会写入
mysql.innodb_table_stats,而存在mysql.column_statistics表中
长期生效必须关掉“统计信息过期静默容忍”
MySQL 8.0 默认启用 information_schema_stats_expiry(单位秒),值为 86400(24 小时)。这意味着即使你刚跑完 ANALYZE TABLE,只要超过这个时间没再更新,优化器就会回退到硬编码的“安全估算值”,比如一律按 1000 行算——这比没统计信息还危险。
- 立即关闭过期机制:
SET GLOBAL information_schema_stats_expiry = 0(0 表示永不过期) - 该设置需写入配置文件
my.cnf的[mysqld]段落,否则重启失效 - 注意:设为 0 后,所有统计信息将严格依赖你主动执行的
ANALYZE TABLE或直方图更新,没有兜底
真正容易被忽略的是:升级后首次 ANALYZE TABLE 的结果,会被持久化并影响后续所有查询;但如果表结构在升级过程中被重建(如通过 mysqldump 导入),旧直方图会丢失,且不会自动重建——得人工补上,否则偏斜字段永远走错路。

















