← 中文资源

简体中文版本

慢查询清除工作流

按总耗时(频率乘时长)排名慢查询,用索引或改写修头部惯犯,并对照生产负载形态验证。

阅读英文原文

目标

按时长排序修的是戏剧化的离群值,按总耗时(调用数 × 均值)排序修的才是负载。热循环里 50 毫秒的查询远比 4 秒的日报重要,只有总耗时视图能揭示这一点。修完每个惯犯都要在生产量级的数据上验证计划成立,并加计划回归守卫。

步骤要点

  1. 按总耗时排名:聚合语句统计按 total_exec_time 排序,前几行就是数据库时间的最大占用者。
  2. 分析头部惯犯:对生产量级数据跑 EXPLAIN (ANALYZE),指出计划缺陷——缺索引、行数误估、排序溢出。
  3. 精准修复缺陷:谓词形态匹配的复合索引、消除包裹列的函数、游标分页替代 offset 扫描。
  4. 生产形态下验证:在预发用生产量级数据加流量回放测试,确认计划在真实基数下依然成立。
  5. 带回归守卫上线:部署后观察该语句总耗时下降,并对触碰过的查询加计划回归检查。

排错提示

lower(name) 这类列上函数让索引静默失效,查询能跑能返回,却在总耗时榜首坐几个月;深翻页 offset 读取并丢弃五万行,每翻一页线性变差,换游标分页让深页恒定成本;修复在预发有效生产失效是基数不同,验证必须用生产量级数据。

适用边界

总耗时排名依赖统计累积周期,刚重启的库数据不足需等待;索引修复增加写放大,写密集表要一并评估写入代价。

代码示例

-- ranked by total time
SELECT round(total_exec_time) AS total_ms,
       calls, round(mean_exec_time, 1) AS mean_ms,
       left(query, 60) AS q
FROM pg_stat_statements
ORDER BY total_exec_time DESC LIMIT 5;

--  412,088   102,411   4.0   SELECT * FROM items
--                                  WHERE lower(name) = $1
--   88,214    14,310   6.2   ... offset pagination ...

-- fix #1: the lower() wraps the column -> index unusable
--   functional index: CREATE INDEX ON items (lower(name));
--   mean 4.0ms -> 0.11ms  total: 412s -> 11s per day
-- fix #2: keyset pagination replaces OFFSET deep scans