导读:本期聚焦于俊华创作的《如何容器化部署 PostgreSQL 的 pg_stat_statements 性能监控扩展?》,敬请观看详情。pg_stat_statements 是 PostgreSQL 官方提供的一款性能分析扩展,它能够记录数据库中所有 SQL 语句的执行统计信息,包括调用次数、总耗时、缓存命中率等关键指标,是定位慢 SQL 和优化数据库性能的利器。而在容器化环境中使用它时,不少使用者会遇到扩展未预装、参数配置不生效、统计结果不准确等问题。本文将围绕 Docker 环境下的完整实践展开,从编写 Dockerfile 定制镜像、docker-compose 配置启动参数,到进入容器启用扩展、配合查询语句分析慢 SQL,逐步讲解每个环节的原理和常见坑点,帮助你在容器里轻松搭建一套可用的 SQL 监控体系。

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

如何容器化部署 PostgreSQL 的 pg_stat_statements 性能监控扩展?

为什么 pg_stat_statements 必须预加载

PostgreSQL 的扩展分为两类:一类是纯 SQL 层面的扩展,通过 CREATE EXTENSION 即可随时安装;另一类则需要在后端进程启动时注入共享内存,pg_stat_statements 就属于后者。它在数据库启动时会申请一块共享内存区域,用于存放所有 SQL 语句的统计信息,所有后端进程共同读写这块区域。如果不在 shared_preload_libraries 中声明,这块共享内存根本不会被创建,此时执行 CREATE EXTENSION 虽然能把扩展对象安装到系统目录中,但查询视图时会直接报错,提示模块未被预加载。

理解了这一点,就能明白容器化部署的核心问题:如何让 postgres 容器在启动时就带上正确的启动参数。官方 postgres 镜像提供了一个便捷机制,凡是名为 POSTGRES_INITDB_ARGScommand 的配置项都可以覆盖默认启动命令,而通过 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

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260902/49089.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。