
PostgreSQL 的 pg_stat_statements 扩展本身并不直接提供 “shared plan cache hit rate”(共享执行计划缓存命中率)这一指标。这个术语容易引起误解——PostgreSQL 并没有一个独立、全局、可被直接监控的“shared plan cache”机制,像 Oracle 的 shared pool 或 SQL Server 的 plan cache 那样显式缓存已编译执行计划供跨会话复用。
为什么没有真正的“shared plan cache hit rate”?
PostgreSQL 的执行计划缓存行为是会话级且隐式的:
- 每个后端进程(会话)在首次执行某条语句时,会解析、重写、规划并生成执行计划,并将该计划缓存在本会话的内存中(
Portal和PreparedStatement结构内); - 同一会话内重复执行相同语句(尤其通过
PREPARE/EXECUTE或简单重复SELECT)可复用该计划,属于“session-level plan cache hit”; - 不同会话之间不共享执行计划——即使两条语句字面完全相同,两个会话各自生成的计划也彼此独立,不存在跨会话的“shared plan cache”;
-
pg_stat_statements记录的是语句级统计(如调用次数、总耗时、平均时间等),但不记录单次执行是否复用了本会话已有计划,更不跟踪跨会话计划复用(因为根本不存在)。
实际可监控的、与计划缓存相关的有效指标
虽然无法监控“shared plan cache hit rate”,但以下指标能间接反映计划生成开销和缓存利用效率:
-
pg_stat_statements.total_plan_time:所有执行中“计划生成阶段”的总耗时(单位 ms)。若该值占total_exec_time比例显著升高(例如 >5%~10%),说明频繁生成新计划,可能因语句未参数化、绑定变量缺失或大量 ad-hoc 查询导致; -
pg_stat_statements.calls与pg_stat_statements.rows的比值:低 calls / high rows 可能表示大查询被反复执行,若未使用PREPARE,每次都会重新规划; -
高
pg_stat_statements.min_plan_time或max_plan_time:表明某些执行的计划生成特别慢,可能涉及复杂视图展开、大量分区裁剪或统计信息陈旧; -
结合
pg_prepared_statements视图:检查当前有多少预编译语句活跃(SELECT count(*) FROM pg_prepared_statements),以及它们的生命周期——长期存在的 prepared statement 是会话级计划复用的良好实践。
如何降低计划生成开销(即提升“事实上的”计划缓存效率)
目标是让每个会话尽可能复用已有计划,减少 plan_time 占比:
- 应用层优先使用
PREPARE+EXECUTE模式,尤其对高频、参数化查询; - 避免拼接 SQL 字符串(string concatenation)生成动态查询,改用参数占位符(
$1, $2); - 合理设置
prepare_statement(如 JDBC 的prepareThreshold),对重复执行 ≥5 次的语句自动预编译; - 定期运行
ANALYZE,确保优化器能快速生成稳定、高效的计划,避免因统计滞后导致反复重规划; - 监控
pg_stat_statements.query中出现大量相似但字面不同的语句(如带不同字面值的 WHERE 条件),这是参数化不足的典型信号。
一个实用的监控查询示例
以下查询可帮你识别计划开销异常高的语句:
SELECTquery,
calls,
round(total_plan_time::numeric / nullif(total_exec_time, 0) * 100, 2) AS plan_time_pct,
round(total_plan_time::numeric / nullif(calls, 0), 2) AS avg_plan_ms,
round(total_exec_time::numeric / nullif(calls, 0), 2) AS avg_exec_ms
FROM pg_stat_statements
WHERE total_exec_time > 0 AND calls > 10
ORDER BY plan_time_pct DESC
LIMIT 10;










