UPDATE后统计信息不更新,优化器会沿用旧快照“瞎猜”数据分布,导致基数估算错误、索引选择失当、执行计划退化;SQL Server需手动执行UPDATE STATISTICS,MySQL须显式ANALYZE TABLE,二者均不因普通UPDATE自动触发更新。

UPDATE后统计信息不更新,优化器会“瞎猜”数据分布
UPDATE语句本身不触发统计信息自动更新(哪怕改了几百万行),优化器仍沿用旧的统计快照做成本估算。比如原统计显示status列只有100个不同值,UPDATE把其中90%的行改成新值'processed'后,实际唯一值只剩2个,但优化器还按“高选择性”判断,错误地认为走索引比全表扫描更优——结果选了低效索引,rows预估从1万变成100万,执行计划直接退化。
SQL Server和MySQL处理逻辑差异明显
SQL Server默认在数据变更量超阈值(500 + 20%)时触发自动更新,但批量UPDATE常因事务原子性绕过该机制;MySQL 8.0开启innodb_stats_auto_recalc = ON也只对DDL或大比例DML生效,普通UPDATE完全不触发。两者共同点是:都不会把单条UPDATE当作统计更新信号。
- SQL Server需手动跑
UPDATE STATISTICS table_name或sp_updatestats - MySQL必须显式执行
ANALYZE TABLE table_name,mysql_upgrade无效 - PostgreSQL则依赖
VACUUM ANALYZE,单纯UPDATE后不运行就维持旧统计
容易被忽略的“假正常”现象
查询没报错、索引还在、执行计划看起来“有索引”,不代表统计信息有效。常见误导信号包括:
-
EXPLAIN显示type=ref但rows值离谱(如表10万行,预估rows=800万) - 同一WHERE条件,不同时间执行耗时波动极大(300ms ↔ 8s),且
key字段不变 - SQL Server中
STATS_DATE()返回时间早于最近一次大批量UPDATE时间 - MySQL的
INFORMATION_SCHEMA.INNODB_TABLESTATS.last_update停留在升级或建表时刻
什么时候必须手动干预?
别等慢查询报警才行动。以下场景一做完就得跟ANALYZE TABLE或UPDATE STATISTICS:
- 执行完单次影响超10%行数的UPDATE/DELETE(尤其涉及高频查询字段)
- ETL任务加载新分区或追加大量历史数据后
- MySQL跨大版本升级(5.7→8.0)、SQL Server打SP补丁后
- 发现
COUNT(DISTINCT col)结果与SHOW INDEX里Cardinality严重不符
统计信息不是“设好就忘”的配置,它是动态快照——数据变了,地图就得重画,否则优化器永远在旧地图上开车。

















