同一条 SQL 突然变慢
语句一个字没改,执行时间却从几十毫秒涨到几秒,绝大多数不是数据库「随机抽风」,而是执行计划变了。代价模型依赖统计信息、数据分布、可用索引与传入参数,任何一项变化都可能让优化器改选另一条路径。排查顺序通常是从估算与实际的差距开始,再依次检查统计信息新鲜度、数据倾斜、索引变更,最后才考虑参数化带来的计划分化。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 12 及以上 |
|---|---|
| 首要证据 | 分析模式输出的估算行数与实际行数对比 |
| 常见原因 | 统计信息过期、数据分布变化、索引增减、参数取值差异 |
| 临时手段 | 强制索引或提示语句用于验证方向与应急恢复 |
| 根治方向 | 更新统计、调整索引结构、必要时固定语句形态 |
典型使用场景
- 线上接口耗时突然上升,需要确认是否与计划变化相关
- 数据导入或大促后,报表类语句开始明显变慢
- 新索引上线后,另一批语句反而变慢(代价模型被改变)
- 预编译语句在不同参数下表现差异巨大,需要区分计划类型
- 建立回归比对机制:保留关键语句的计划快照,变更前后对比
排查步骤示例
排查步骤示例
-- 第一步:看估算与实际行数的差距,落差大说明统计信息或分布有问题
EXPLAIN ANALYZE SELECT ... ;
-- 第二步:MySQL 侧更新统计信息后复看
ANALYZE TABLE orders;
-- 第三步:PostgreSQL 侧验证参数差异导致的计划分化
EXPLAIN (ANALYZE) SELECT * FROM orders WHERE user_id = 100; -- 选择性高的参数
EXPLAIN (ANALYZE) SELECT * FROM orders WHERE user_id = 1; -- 选择性低的参数
-- 预编译语句可能复用不适合当前参数的缓存计划
SHOW plan_cache_mode; -- 查看当前策略
-- 需要时可按会话临时切换为强制自定义计划,验证是否由缓存计划引起
-- 第四步:必要时用强制索引验证方向,确认后再改语句或索引结构常见原因对照
| 原因 | 现象 | 处理方向 |
|---|---|---|
| 统计信息过期 | 估算与实际差距大 | 重新采样统计信息 |
| 数据分布变化 | 含倾斜取值的数据变多 | 提高统计精度或改写语句形态 |
| 索引增减 | 旧语句选了新路径 | 复核索引组合,避免功能重叠 |
| 参数取值极端 | 同一语句快慢因参数而异 | 区分计划类型或改写为等值加范围 |
| 缓存计划失真 | 预编译语句长期走坏计划 | 调整计划缓存策略 |
分析模式会真实执行语句,评估前先确认它属于读操作,或把语句包在事务里执行后立刻回滚。生产环境的执行代价要提前估算。
把关键语句的计划与耗时定期归档,是判断「计划突变」的前提。没有历史快照时,只能靠猜测,排查周期会被拉长很多。