pg_stat_statements 是 PostgreSQL 中最常用的性能诊断扩展之一,它会把数据库执行过的每一条 SQL 语句按查询指纹归类,记录调用次数、总耗时、平均耗时、共享内存读取量等统计信息。对于需要持续优化数据库性能的团队来说,几乎是必装组件。然而在容器化部署时,官方 postgres 镜像默认并不启用这个扩展,而且它的预加载方式与其他扩展不同,必须通过 shared_preload_libraries 参数在数据库启动阶段加载,直接执行 CREATE EXTENSION 会报错。这篇文章就来完整讲讲如何在 Docker 环境中正确部署和使用 pg_stat_statements。

为什么 pg_stat_statements 必须预加载
PostgreSQL 的扩展分为两类:一类是纯 SQL 层面的扩展,通过 CREATE EXTENSION 即可随时安装;另一类则需要在后端进程启动时注入共享内存,pg_stat_statements 就属于后者。它在数据库启动时会申请一块共享内存区域,用于存放所有 SQL 语句的统计信息,所有后端进程共同读写这块区域。如果不在 shared_preload_libraries 中声明,这块共享内存根本不会被创建,此时执行 CREATE EXTENSION 虽然能把扩展对象安装到系统目录中,但查询视图时会直接报错,提示模块未被预加载。
理解了这一点,就能明白容器化部署的核心问题:如何让 postgres 容器在启动时就带上正确的启动参数。官方 postgres 镜像提供了一个便捷机制,凡是名为 POSTGRES_INITDB_ARGS、command 的配置项都可以覆盖默认启动命令,而通过 command 追加 -c shared_preload_libraries=pg_stat_statements 是最简单的做法。另一种更规范的方式是挂载自定义配置文件,两种方案后面都会给出示例。
用 docker-compose 完成部署
先看最简洁的方案,直接用官方 postgres 镜像,通过 command 传入启动参数。官方镜像从 14 版本开始已经自带 pg_stat_statements 的软件包,无需额外安装,这一点可以通过进入容器执行 ls /usr/share/postgresql/extension/ 验证,如果能看到 pg_stat_statements 相关文件就说明可用。
version: "3.8"
services:
postgres:
image: postgres:16
container_name: pg-stats
environment:
POSTGRES_PASSWORD: mysecretpassword
POSTGRES_DB: appdb
command:
- "postgres"
- "-c"
- "shared_preload_libraries=pg_stat_statements"
- "-c"
- "pg_stat_statements.track=all"
- "-c"
- "pg_stat_statements.max=10000"
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:这里配置了三个关键参数。shared_preload_libraries 负责预加载扩展,是必须项;pg_stat_statements.track 设为 all 表示追踪所有语句,包括存储过程内部的语句,如果只关心顶层语句可以设为 top,设为 none 则暂停记录;pg_stat_statements.max 控制记录的查询指纹数量上限,默认 5000,超出的新指纹会挤掉最久未使用的记录,生产环境建议根据业务复杂度适当调大。注意 max_track_size 之外还有一个 pg_stat_statements.track_utility 参数,决定是否记录 DDL 和工具类命令,按需开启即可。
如果团队有统一的配置管理需求,也可以用挂载配置片段的方式替代 command 参数。官方镜像在容器启动时会自动读取 /docker-entrypoint-initdb.d/ 目录下的初始化脚本,同时也会合并 /var/lib/postgresql/data/ 之外的 postgresql.conf 包含目录。更简单的做法是利用 include_dir 机制,把自定义参数写进一个独立文件:
# 挂载一个自定义配置目录 volumes: - pgdata:/var/lib/postgresql/data - ./conf:/etc/postgresql/conf.d # 修改镜像默认配置,追加包含目录 command: - "postgres" - "-c" - "config_file=/var/lib/postgresql/data/postgresql.conf" - "-c" - "include_dir=/etc/postgresql/conf.d"
然后在本地 ./conf/tuning.conf 文件中写入参数:
shared_preload_libraries = 'pg_stat_statements' pg_stat_statements.track = all pg_stat_statements.max = 10000 pg_stat_statements.track_utility = off
这种方式的好处是配置与编排文件分离,参数修改不需要改动 compose 文件,便于纳入 Git 管理和做配置审计。
启用扩展并验证统计功能
容器启动成功后,扩展还只是被加载了,并没有在具体数据库中激活。需要连接到目标数据库执行一次 CREATE EXTENSION,这一步会创建 pg_stat_statements 视图和相关函数。可以进入容器操作:
# 进入容器 docker exec -it pg-stats psql -U postgres -d appdb # 在目标数据库中启用扩展 CREATE EXTENSION IF NOT EXISTS pg_stat_statements; # 验证扩展已安装 SELECT extname, extversion FROM pg_extension WHERE extname = 'pg_stat_statements'; # 执行几条测试查询后查看统计 SELECT query, calls, total_exec_time, mean_exec_time, shared_blks_read, shared_blks_hit FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;
如果希望容器首次初始化时自动完成这一步,可以把 SQL 放进初始化脚本目录,镜像初始化数据库时会自动执行:
# 项目目录结构 # ./init/01-enable-extension.sql # ./docker-compose.yml volumes: - pgdata:/var/lib/postgresql/data - ./init:/docker-entrypoint-initdb.d
对应的 01-enable-extension.sql 内容为:
-- 初始化时在默认数据库中启用扩展 CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
需要注意的是,初始化脚本只在数据目录为空、即首次创建数据库集群时执行一次。如果容器之前已经运行过并且保留了数据卷,后挂载的初始化脚本不会生效,此时必须手动连接每个需要监控的数据库执行创建语句,因为 CREATE EXTENSION 是库级别的操作,只在当前数据库可见。
常见报错与排查思路
第一个高频报错是执行查询时提示 pg_stat_statements must be loaded via shared_preload_libraries,这说明预加载参数没生效。排查时先执行 SHOW shared_preload_libraries; 确认运行时值,如果为空,多半是 command 参数写法问题或者修改配置后没有重启容器。这里有个容易踩的坑:在已经运行的容器里用 ALTER SYSTEM 修改这个参数虽然可以写入,但它是启动型参数,必须重启 PostgreSQL 进程才会生效,而很多人执行完后以为立即生效就直接建扩展,结果报错。
第二个问题是统计视图里只有寥寥几条记录或者数据不更新。可能的原因有三种:一是 pg_stat_statements.track 被设成了 none 或 top,导致部分语句未被追踪;二是查询走的是预备语句且指纹归类逻辑把多条语句合并了,看起来数量偏少;三是统计信息存储在共享内存中,数据库正常关闭时会持久化,但强制杀容器进程可能丢失部分最新数据。此外,如果需要清空历史统计重新观察,可以执行 SELECT pg_stat_statements_reset();,它会清零所有计数,适合在压测前使用,以便获得干净的基线数据。
第三个常见需求是从宿主机或应用容器访问统计信息。只要数据库端口已经映射出来,用任何 PostgreSQL 客户端连接后查询该视图即可,监控组件如 postgres_exporter 也提供了抓取 pg_stat_statements 的配置项,可以将慢 SQL 指标直接接入 Prometheus 和 Grafana 做长期可视化,这比人工登录容器查日志要高效得多,也是容器化场景下做数据库可观测性的主流做法。
性能开销与使用建议
pg_stat_statements 并非零成本,它会对每条语句的解析和执行做额外记录,官方文档给出的开销通常在个位数百分比,多数业务可以接受,但对延迟极度敏感的系统仍需评估。可以通过 pg_stat_statements.track 的取值在精度和开销之间取舍,日常监控用 top 就够了,排查存储过程内部问题时再临时切换到 all 并重启。
另一个建议是结合 pg_stat_activity 一起使用。pg_stat_statements 反映的是历史累计统计,适合找出最耗时的 Top SQL;而 pg_stat_activity 是实时快照,能看到当前正在执行的语句和等待事件。两者配合,先通过累计统计定位嫌疑 SQL,再用实时视图观察它的执行行为,基本可以覆盖大部分性能排查场景。同时建议定期把统计快照落库,形成时间序列数据,这样才能发现性能随时间的劣化趋势,而不是只看某个瞬间的静态结果。
pg_stat_statementsPostgreSQLDocker修改时间:2026-09-02 19:11:14