Mikhail Shytsko:四十条删除记录从 pg_stat_statements 中消失了

Mikhail Shytsko:四十条删除记录从 pg_stat_statements 中消失了

💡 原文英文,约3400词,阅读约需13分钟。
📝

内容提要

实测PostgreSQL 18的pg_stat_statements:同一问题因SQL写法不同会拆成多条记录,租户schema和SET值被合并,视图无application_name列,无法区分应用与代理;函数内语句默认不记录,报错语句不计数;超5000条会淘汰低频记录,日志可补足。

🔎

延伸解读

查询指纹的合并与分裂:格式化友好,但语义等价不友好

pg_stat_statements 在计算 queryid 前会剥离常量,因此字面量不同的 SQL 会合并。但任何解析树变化都会分裂条目,例如交换 AND 条件顺序、使用 count(1) 或表别名。这意味着由 AI 代理生成的 SQL 即使语义相同,也可能因写法波动产生多条记录,增加统计噪声。

多租户场景下的统计模糊:schema 与 SET 值不再区分

PostgreSQL 18 将不同 schema 中同名表的查询合并到同一 queryid,且 SET 自定义参数的值被替换为占位符。这导致租户级别的统计信息丢失,无法从视图中区分不同租户的负载。升级前需评估对监控和故障排查的影响。

视图的盲区:application_name 缺失与错误语句不记录

pg_stat_statements 视图没有 application_name 列,无法区分同一角色下的不同应用或代理。此外,执行失败的语句不会记录,函数内语句默认也不记录。若需追踪这些信息,需依赖日志并配置 log_line_prefix 包含 %u 和 %a。

淘汰机制与容量规划:低频语句易被清除

当不同语句形状超过 pg_stat_statements.max(默认 5000)时,执行次数最少的条目会被淘汰。代理产生的大量一次性语句容易成为淘汰对象,而高频应用查询通常保留。规划容量时应按语句形状数而非原始语句数估算,并考虑启用 track = all 会增加条目数。

❓

Q&A

为什么我的查询在 pg_stat_statements 中消失了?

查询消失可能有几个原因:报错的语句根本不会被记录,因为记录发生在执行完成时;如果语句嵌套在函数、过程或 DO 块中且 pg_stat_statements.track 为默认的 top,视图只保留客户端发送的内容;如果条目存在但被淘汰以腾出空间,pg_stat_statements_info.dealloc 会统计丢弃次数但不指明具体是哪些。

pg_stat_statements 中不同 SQL 写法会导致重复条目吗?

会。任何改变解析树的变化都会拆分条目。例如,将 count(*) 改为 count(1)、使用表别名、交换谓词顺序、使用子查询或 CTE 都会产生独立条目。而小写关键字、重新格式化空白、前导注释和添加 public. 前缀则会合并到基线条目中。

pg_stat_statements 能区分不同的 application_name 吗?

不能。视图没有 application_name 列,其分组键仅包括 userid、dbid、toplevel 和 queryid。pg_stat_activity 会为当前连接的会话携带 application_name,日志在 log_line_prefix 包含 %a 时也会记录它。若要在视图内持久区分,调用者需要拥有自己的角色。

提高 pg_stat_statements.max 需要重启数据库吗?

需要。因为该参数具有 postmaster 上下文,且条目存储在启动时确定大小的共享内存区域中,重新加载无法调整其大小。提高该值需要重启,并且要提前预算内存。另外,将 pg_stat_statements.track 切换为 all 会为每个不同的嵌套形状增加一行,它们都会竞争同一个上限。

pg_stat_statements 中多租户 schema 的查询会合并吗?

在 PostgreSQL 18 中,不同 schema 下同名的表会共享一个 queryid,即使显式指定 schema 也不会分开。例如 tenant_a.invoices 和 tenant_b.invoices 的查询会合并到同一行,计时被平均。而 PostgreSQL 17 会为它们保留独立的条目。

函数或 DO 块内的语句会被 pg_stat_statements 记录吗?

默认不会。pg_stat_statements.track 默认为 top,只记录客户端发送的语句,不记录服务器内部执行的语句。因此,函数或 DO 块内的 SELECT 不会单独出现。将 track 设置为 all 可以记录嵌套语句,但它们会带有 toplevel=false 并与其他语句竞争相同的 5000 个槽位。

如何查看 pg_stat_statements 中已删除的语句?

视图本身无法显示已删除的语句,但可以通过日志来补充。启用 log_min_duration_statement 并设置 log_line_prefix 包含 %u 和 %a,日志会记录角色、application_name 和字面值,从而提供视图无法保留的详细信息。

pg_stat_statements 中报错的语句会被计数吗?

不会。记录发生在语句执行完成时,报错的语句没有完成执行,因此不会被记录。例如,语法错误、除零错误或违反唯一约束的语句都不会出现在视图中。

🏷️

标签

➡️

继续阅读