Ryan Booz:生产环境中的 Postgres 专题系列:如何查询 pg_stat_statements 以找出缓慢且昂贵的 Postgres 查询(第 7 部分)

Ryan Booz:生产环境中的 Postgres 专题系列:如何查询 pg_stat_statements 以找出缓慢且昂贵的 Postgres 查询(第 7 部分)

💡 原文英文,约3200词,阅读约需12分钟。
📝

内容提要

本文是Postgres监控系列终篇,介绍如何查询pg_stat_statements定位问题。故障时应先查pg_stat_activity查看当前活动,而非pg_stat_statements,因为后者只记录已完成查询的累计数据。获取时间窗口有两种方法:间隔取两次快照做差值,或重置指标重新观察。可按总执行时间、调用次数、平均耗时、磁盘读写等维度排序,最慢查询未必是真正问题。建议使用监控工具保存历史趋势。

🔎

延伸解读

故障排查第一步:为何先查 pg_stat_activity

文章强调,当数据库出现性能问题时,不应直接查询 pg_stat_statements,而应首先查看 pg_stat_activity。因为 pg_stat_statements 只记录已执行完成的查询的累计指标,无法反映当前正在运行的长事务或异常查询。通过 pg_stat_activity 可以实时查看活动会话、查询持续时间、等待事件等,快速判断是否存在异常长查询,从而决定是否需进一步分析 pg_stat_statements。

两种获取时间窗口的方法:快照差值与重置

由于 pg_stat_statements 数据是累计的,没有时间线,文章给出两种获取时间窗口的方法:一是间隔取两次快照并计算差值,适合需要保留历史数据的场景;二是重置指标(pg_stat_statements_reset)后重新观察,适合高并发且可重复的故障排查。重置会清空所有累计数据,因此需谨慎使用,并可通过 pg_stat_statements_info 查看上次重置时间。

排序维度决定排查方向:最慢查询未必是问题

文章指出,通过不同的 ORDER BY 子句可以从多个维度识别问题查询:按总执行时间、调用次数、平均执行时间、磁盘读取或临时块写入等。最慢的查询不一定是性能瓶颈,一个执行很快但调用次数极多的查询可能累计消耗大量资源。此外,频繁写入临时块的查询可能因 work_mem 不足导致磁盘 I/O 压力,值得关注。

监控工具的选择与注意事项

文章建议使用监控工具保存 pg_stat_statements 的历史趋势,以便回溯和分析。选择工具时需注意:是否捕获完整查询文本、快照频率是否过高导致锁竞争、能否优雅处理重置。多个工具同时查询 pg_stat_statements 可能增加轻量级锁争用,影响应用性能。开源选项如 pgwatch、PgHero、pg_statviz 也可考虑,或自行构建但需处理快照、历史存储和重置等问题。

Q&A

数据库出现性能问题时,为什么应该先查 pg_stat_activity 而不是 pg_stat_statements?

因为 pg_stat_statements 只记录已执行完成的查询的累计指标,不包含当前正在运行的查询。如果问题是一个尚未完成的临时查询,pg_stat_statements 中可能没有它的数据。而 pg_stat_activity 可以显示当前活动的进程,包括查询已运行的时间、等待事件等,帮助你判断是否有异常的长查询正在运行。

如何在不重置 pg_stat_statements 的情况下获取一段时间内的查询性能变化?

可以间隔一段时间取两次 pg_stat_statements 的快照(例如创建临时表保存两次查询结果),然后对两次快照中关心的指标(如 calls、total_exec_time、rows、shared_blks_read、temp_blks_written 等)做差值计算,并按差值排序,从而找出该时间段内执行时间最长、调用最多或读写最多的查询。

pg_stat_statements_reset() 有什么作用?什么时候适合使用?

pg_stat_statements_reset() 会清空所有累计指标和查询记录,从零开始重新收集。它适合在正在发生且可重复的高负载故障期间使用,以创建一个干净的观察窗口。但如果问题很少出现,或者你需要保留历史数据,则应避免使用。重置后可以通过查询 pg_stat_statements_info 查看上次重置时间。

查询 pg_stat_statements 时,为什么最慢的查询不一定是最需要优化的?

因为一个平均执行时间很短的查询,如果被调用了成千上万次,其总执行时间可能非常大,对系统造成累积负担。相反,一个单次很慢但调用次数很少的查询,总影响可能较小。因此需要根据不同的排序维度(如总执行时间、调用次数、平均耗时、磁盘读写等)来综合判断,而不是只看单次最慢的查询。

在 pg_stat_statements 中,可以通过哪些排序方式来定位不同类型的性能问题?

可以按 total_exec_time 降序找出总执行时间最长的查询;按 calls 降序找出调用最频繁的查询;按 mean_exec_time 降序(可加 calls >= 10 过滤)找出每次执行都慢的查询;按 shared_blks_read 降序找出读取磁盘最多的查询;按 temp_blks_written 降序找出写入临时文件最多的查询(可能因 work_mem 不足导致)。

选择 Postgres 监控工具时,应该注意哪些问题?

需要注意:工具是否能捕获完整的查询文本(避免截断);快照 pg_stat_statements 的频率是否过高,以免增加锁竞争;如果执行 pg_stat_statements_reset(),工具能否优雅地处理重置(从零恢复);以及是否与托管服务商或其他工具同时查询 pg_stat_statements 导致额外的轻量级锁竞争。

🏷️

标签

➡️

继续阅读