INSERT后统计信息立刻不准,因其依赖采样或快照而非实时数据;批量插入50万行等场景使直方图缺失新数据分布,导致优化器误判选择性而放弃索引。

因为优化器“看的不是真实数据,而是过期地图”——INSERT 后不更新统计信息,会导致执行计划严重失准,哪怕索引还在、SQL 没改,查询也可能从毫秒级退化到分钟级。
INSERT 后统计信息为什么立刻就“不准”了?
统计信息不是实时镜像,它靠采样或历史快照估算数据分布。一次批量插入 50 万行,sys.dm_db_stats_properties 中的 rows 和 modification_counter 可能完全没变,但直方图里根本没这批新数据的值域、频次、边界信息。尤其当新数据集中在某段范围(如时间戳递增、订单号升序),而旧统计只覆盖历史低值区间时,优化器会严重低估 WHERE 条件的选择性,误判为“查不到几行”,从而放弃走索引,改用全表扫描。
哪些 INSERT 场景最需要立即手动更新?
自动更新机制在此类场景下基本失效,必须人工干预:
- 单次 INSERT 超过表当前总行数的 10%(例如千万行表插 100 万+)
- 向升序/降序列(如
created_at、order_id)持续追加数据,且该列上有索引或常用于过滤 - ETL 批量加载后,特别是分区表新增分区或历史表归档迁移完成
- INSERT 带有明显数据倾斜(如大量插入
status = 'pending',而原统计中该值占比极低)
UPDATE STATISTICS 怎么写才不踩坑?
别一上来就 EXEC sp_updatestats 全库扫——IO 高、锁表久、还可能把刚生成的优质采样冲掉。更稳妥的做法是:
- 先定位:查
sys.dm_db_stats_properties,筛出modification_counter > 50000或last_updated 的统计对象 - 再精准更新:对高频查询表,优先用
UPDATE STATISTICS dbo.Orders WITH SAMPLE 30 PERCENT;对状态字段、分区键等分布敏感列,加FULLSCAN - 注意依赖:如果刚执行过
ALTER INDEX ... REBUILD,它默认已更新关联统计,无需重复操作
MySQL 的 ANALYZE TABLE 也得盯紧时机
MySQL 不叫“更新统计信息”,但行为一致:ANALYZE TABLE 是它的等效操作。默认采样率有限,大表上它可能只读几十个页,对突增的数据覆盖不足。尤其要注意:
- InnoDB 表在
innodb_stats_persistent = OFF时,统计信息重启后丢失,必须重做 - 分区表要单独对每个分区
ANALYZE TABLE t PARTITION(p2024),否则主表统计仍是空壳 -
ANALYZE TABLE期间会加元数据锁(MDL),在高并发写入场景下可能阻塞 DML,建议避开业务高峰
真正容易被忽略的点是:INSERT 完成 ≠ 数据就“可见于优化器”。中间隔着一层统计信息缓存。很多慢查询排查到最后,发现不是索引没建、不是 SQL 写错,就是那张表在凌晨三点批量导入后,没人去碰过 UPDATE STATISTICS 或 ANALYZE TABLE ——地图没更新,车当然开错路。










