GaussDB 慢查询调优:一条 SQL 突然变慢,可能只是统计信息“过期”了
线上一条跑了半年的核心查询 SQL,某天开始响应时间从几十毫秒涨到了好几秒,业务侧开始零星超时。EXPLAIN 一看,执行计划跟之前完全不同——原本走索引扫描,现在变成了全表扫描。第一反应是索引失效或者数据量膨胀,但表结构没动,索引也在,数据量增长也在预期范围内。最后定位到的根因很朴素:统计信息过期了。
一、统计信息为什么能让执行计划“翻脸”
GaussDB 的优化器是基于代价的优化器(CBO) ,它靠统计信息(表的行数、列的 distinct 值数量、数据分布直方图等)来估算每种执行计划的代价,选最低代价的方案执行。统计信息不是实时更新的——它依赖 ANALYZE 触发采集,而自动采集有周期。如果表的写入模式发生了变化(比如某个状态字段的取值分布从均匀变成了极度倾斜),旧的统计信息会让优化器严重误判行数,进而选错 join 方式或扫描方式。
从经验数据看,约 10% 的性能问题根源是统计信息没有收集或已经陈旧。我们这次就是典型的“分布变化但统计没跟上”:某个业务状态字段上线新枚举值后,占比迅速增大,但列上的直方图还是旧分布,优化器仍按“该值极少”来估算,选择了 NestLoop,实际却要扫描大量行。
二、排查路径:先看计划,再对统计
定位慢 SQL 的正确顺序不是上来就加索引,而是先拿真实执行计划。EXPLAIN 只能看优化器的估算,真正要定位偏差,需要用 EXPLAIN ANALYZE 或 EXPLAIN PERFORMANCE 拿到实际执行的行数、耗时和内存,和估算值对比。我们对比后发现,某个扫描算子的 rows 估算值差了近两个数量级——这是统计信息失真的直接信号。
确认后,对相关表执行 ANALYZE 重新采集统计信息,再跑一次 EXPLAIN PERFORMANCE,执行计划回到了索引扫描,耗时恢复到毫秒级。
三、不能每次都靠手动 ANALYZE:建立统计信息治理习惯
手动 ANALYZE 解决单次问题,但要避免反复踩坑,需要把统计信息纳入日常治理:
1. 关注自动采集的覆盖范围。 GaussDB 的自动统计信息收集有默认策略,但对于写入频繁、分布容易倾斜的表,自动采集的频率可能不够。可以针对关键表配置更积极的 autovacuum / autoanalyze 参数,或者在业务低峰期通过调度任务手动触发 ANALYZE。
2. 用 SQL PATCH 做临时止血。 如果统计信息一时无法立刻更新,而线上业务等不起,可以用 SQL PATCH 对特定 SQL 绑定一个已知正确的执行计划,先稳住响应时间,再从容治理统计信息。这是“先止血、后治本”的实用手段。
3. 把“统计信息陈旧”加入慢 SQL 排查清单的第一页。 遇到执行计划突变、估算行数与实际严重不符时,优先检查统计信息的采集时间和数据分布,而不是盲目加索引或改 SQL。
四、总结
GaussDB 慢查询调优的完整链路是:看真实执行计划 → 对比估算与实际 → 检查统计信息 → 必要时 SQL PATCH 止血 → 建立定期 ANALYZE 机制。索引和 SQL 改写当然重要,但如果统计信息本身是错的,优化器再聪明也选不对路。这条 SQL 恢复之后,我们把核心表的统计信息采集频率纳入了巡检,之后再没出现过同类的“无故变慢”。
- 点赞
- 收藏
- 关注作者
评论(0)