pg_stat_activity 实时会话
pg_stat_activity 是 PostgreSQL 所有监控手段里最常用、性价比最高的一个视图。它不需要安装任何扩展,也不需要修改任何配置,只要能连上数据库,就能看到「此刻服务器上每一个后端进程正在做什么」:是谁、从哪台机器连进来的、正在执行哪条 SQL、处于什么状态、在等什么资源、事务从什么时候开始。几乎所有线上故障的第一现场排查——CPU 打满、连接暴涨、SQL 卡住、事务长期不提交——都从这里开始。
它是一个集簇级(cluster-wide)视图:一行对应一个 server 进程,包含本集簇内所有数据库的连接,而不仅仅是你当前连接的数据库。这一点经常被误解。它同时也是实时视图:每次查询都会从共享内存重新读取最新状态,不存在「统计延迟」或「需要刷新」的问题,看到的永远是此刻。
本页是这个监控章节的起点。先把字段、状态、等待事件这三块地基打牢,后面的锁监控(pg_locks)、SQL 统计(pg_stat_statements)都会反复引用这里的概念。
前置知识
建议先阅读:数据脱敏方案
基础概念
PostgreSQL 采用「每连接一个进程」的模型:客户端每建立一个连接,服务端就 fork 一个 backend 进程为其服务,此外还有 checkpointer、background writer、WAL writer、autovacuum launcher、autovacuum worker、walsender 等一批辅助进程。pg_stat_activity 的设计就是「一行 = 一个进程」,把这些进程的实时状态全部暴露出来。
| 概念 | 说明 |
|---|---|
| backend | 为一个客户端连接服务的后端进程,是视图的主要观察对象 |
| 辅助进程 | checkpointer、background writer、WAL writer 等常驻进程,也各占一行 |
| walsender | 流复制/逻辑复制场景下的发送进程,同样出现在视图里 |
| state | 会话当前的宏观状态:执行中、空闲、空闲事务等 |
| wait_event_type / wait_event | 进程此刻在等什么:一对「等待事件类型 + 等待事件」标签 |
| backend_xid / backend_xmin | 进程持有的事务 ID 与最小事务快照(决定能否 VACUUM 的关键) |
| pg_read_all_stats | 预定义角色,非超级用户想看全量字段需要授予它 |
理解这个视图的三个前提:
- 它是实时快照,不是历史记录。 视图没有时间维度,查两次结果不同是正常的。想留历史就必须自己周期性采样(脚本、pg_stat_statements 或外部监控系统)。
- 它是集簇级的。 无论连到哪个数据库执行查询,都能看到整个集簇全部数据库的会话。要只看某个库,需要自己加
WHERE datname = ...过滤。 - 字段可见性受权限控制。 普通用户能看到所有行,但
query、query_id、backend_xid等属于「他人」会话的字段会被置空;授予预定义角色pg_read_all_stats(或超级用户)才能看全。
视图在监控体系中的位置
PostgreSQL 的统计视图可以分为三层,pg_stat_activity 属于最上层、最即时的那一层:
| 层次 | 代表视图 | 回答的问题 | 数据性质 |
|---|---|---|---|
| 实时会话 | pg_stat_activity | 现在谁在连、在干什么、在等什么 | 实时快照 |
| 累计统计 | pg_stat_database、pg_stat_user_tables、pg_stat_user_indexes | 从上次重置到现在累计发生了多少 | 累计计数 |
| SQL 级统计 | pg_stat_statements | 哪些 SQL 最耗时、最耗资源(扩展) | 聚合归一化 |
排查「现在出了什么问题」用第一层;回答「平时哪里最重」用后两层。本页聚焦第一层,后两层分别在本章后续页面展开。
语法与用法
pg_stat_activity 是普通视图,直接 SELECT 即可。最常用的查询形态如下:
-- 1. 看全貌:每个会话的基本信息(常用列)
SELECT pid,
datname,
usename,
application_name,
client_addr,
state,
wait_event_type,
wait_event,
query_start,
query
FROM pg_stat_activity
ORDER BY pid; pid | datname | usename | application_name | client_addr | state | wait_event_type | wait_event | query_start | query
-------+----------+---------+------------------+--------------+-------+-----------------+------------+-----------------------------+-------------------------------
3111 | appdb | app | myapp-pool-1 | 10.0.0.11 | idle | | | 2026-09-30 09:12:04.521+08 | SELECT * FROM orders WHERE ...
3128 | appdb | app | myapp-pool-2 | 10.0.0.11 | active| | | 2026-09-30 09:14:31.002+08 | UPDATE accounts SET balance ...
3140 | postgres | postgres| psql | | active| | | 2026-09-30 09:14:30.988+08 | SELECT pid, ... FROM pg_stat_activity
3155 | appdb | app | autovacuum worker| | active| | | 2026-09-30 09:10:00.113+08 | autovacuum: VACUUM public.logs
(4 rows)-- 2. 只看当前数据库的业务会话(排除自己和后台进程)
SELECT pid, usename, state, wait_event_type, wait_event, query
FROM pg_stat_activity
WHERE datname = current_database()
AND pid <> pg_backend_pid()
AND backend_type = 'client backend';
-- 3. 正在执行的 SQL,按执行时长倒序
SELECT pid,
now() - query_start AS query_duration,
state,
left(query, 80) AS query_head
FROM pg_stat_activity
WHERE state = 'active'
AND backend_type = 'client backend'
ORDER BY query_duration DESC;
-- 4. 看视图的完整定义(确认本版本有哪些字段)
SELECT pg_get_viewdef('pg_stat_activity'::regclass, true);在 psql 里也可以用元命令直接查看列结构:
\d pg_stat_activity后面所有章节的示例都建立在这些基本查询形态之上。
详细说明
字段全览
pg_stat_activity 的字段可以分成五组来记忆。下表以 PG14 及以后版本为准,各版本差异在「版本演进」一节说明。
标识组——这条连接是谁:
| 字段 | 类型 | 说明 |
|---|---|---|
pid | integer | 该后端进程的 OS 进程号,是所有后续操作(kill、关联 pg_locks)的主键 |
datid / datname | oid / name | 该会话连接的数据库 OID 与名称 |
usesysid / usename | oid / name | 登录角色的 OID 与名称 |
application_name | text | 客户端自报的应用名(连接串里可随意设置,需结合连接来源核实) |
client_addr | inet | 客户端 IP;走 Unix 域套接字时为 NULL |
client_hostname | text | 客户端主机名,仅当开启反向 DNS 解析(log_hostname)才有值,一般不用 |
client_port | integer | 客户端 TCP 端口;本地套接字连接为 -1 |
时间组——这条连接的生命线:
| 字段 | 类型 | 说明 |
|---|---|---|
backend_start | timestamptz | 进程启动时间(即连接建立时间) |
xact_start | timestamptz | 当前事务开始时间;不在事务中为 NULL |
query_start | timestamptz | 当前(或最近一条)SQL 开始时间 |
state_change | timestamptz | state 最近一次变化的时间(如从 active 变为 idle 的时刻) |
等待组——它在等什么:
| 字段 | 类型 | 说明 |
|---|---|---|
wait_event_type | text | 等待事件类型,共 9 类,见下一小节 |
wait_event | text | 具体等待事件名;没在等待时两者皆为 NULL |
状态与事务组:
| 字段 | 类型 | 说明 |
|---|---|---|
state | text | 会话宏观状态,六种取值,见下下小节 |
backend_xid | xid | 该后端当前持有的顶层事务 ID;没有则为 NULL |
backend_xmin | xid | 该后端的 xmin 水位,长期不推进会阻碍 VACUUM 清理死元组 |
查询组:
| 字段 | 类型 | 说明 |
|---|---|---|
query | text | 当前正在执行的 SQL;idle 会话显示最近一条;文本已截断处理由内部完成 |
query_id | bigint | 内核计算的查询标识(PG14+),compute_query_id 启用时才有值 |
backend_type | text | 进程类型,如 client backend、autovacuum worker、walsender 等 |
另有 leader_pid(PG13+):若该进程是并行查询的 worker,此列是并行组 leader 的 pid;leader 自身此列为 NULL。它用于把并行 worker 与发起查询的会话关联起来。
state 的六种取值
state 只对 client backend(普通客户端会话)有意义,取值有六种:
| 取值 | 含义 | 运维视角 |
|---|---|---|
active | 正在执行一条 SQL | 数量长期偏高说明 CPU/IO 压力大,重点看 wait_event_type |
idle | 空闲,等待客户端发下一条命令 | 正常状态;数量过大考虑连接池 |
idle in transaction | 打开了事务但当前没有执行中的 SQL | 高危:会长期持有快照与锁,阻塞 VACUUM,重点治理对象 |
idle in transaction (aborted) | 事务内某条语句出错且未回滚,连接仍挂着 | 应用吞了异常没 rollback,处理方式同上 |
fastpath function call | 正在执行 fast-path 函数调用 | 少见,基本不用关心 |
disabled | 被禁用了状态跟踪(track_activities = off) | 检查是谁关了统计开关 |
idle in transaction 是生产环境最常见的「隐形杀手」
一个挂了 20 分钟的 idle in transaction 会话会一直持有事务快照:它的 backend_xmin 之前的所有死元组都无法被 VACUUM 回收(表膨胀),它持有的行锁会让其他会话排队,它的连接白白占用 max_connections 配额。发现它,先看 xact_start 判断挂了多久,再用 pg_terminate_backend() 处理,并通知应用侧修复「开了事务不提交/不回滚」的代码路径。
后台辅助进程(checkpointer、WAL writer 等)不属于客户端会话,state 一般显示为空,不在上述六种取值之列。
等待事件:wait_event_type 的九类
当一个进程「没有在跑代码,而是在等什么东西」时,内核会给它打上一对标签:wait_event_type(等待什么类别)+ wait_event(具体等哪个)。这是排障时信息密度最高的两个字段。
wait_event_type 共有九类:
| 类型 | 在等什么 | 典型场景 |
|---|---|---|
LWLock | 轻量级锁(内部数据结构的短期互斥) | 多并发下的共享内存结构竞争 |
Lock | 重型锁(表锁、行锁、事务锁、顾问锁) | 锁等待、行锁排队 |
BufferPin | 缓冲区被钉住(其他会话长期持有所需缓冲的 pin) | 长事务阻碍 VACUUM 移动元组 |
Activity | 后台进程的空转循环等待 | checkpointer、walwriter 等正常休眠 |
Client | 与客户端交互(读请求/写响应) | 应用不取结果、网络慢、连接假死 |
Extension | 扩展模块自定义的等待点 | 取决于具体扩展 |
IPC | 进程间通信(并行 worker、消息队列等) | 并行查询同步 |
Timeout | 定时器到点(sleep、延迟) | pg_sleep()、vacuum 延迟 |
IO | 文件读写 | 全表扫描、WAL 落盘、排序落盘 |
几点解读经验:
Activity类多见于辅助进程的空转等待,一般是正常现象;业务 backend 出现Client等待要看是不是应用侧处理太慢。Lock+wait_event = transactionid是「会话 A 在等 B 的行锁提交/回滚」的标志,直接跳到锁监控页看阻塞链。IO类集中在DataFileRead、DataFileWrite、WALWrite、WALSync等文件操作上,长期大量出现要怀疑磁盘瓶颈或shared_buffers太小。
常见 wait_event 速查
下列事件名均出自官方文档,属于各类型中出镜率最高的一批。注意不同大版本对部分事件命名做过调整(尤其较新版本把部分驼峰命名改成了连字符风格),以你所用版本的官方 Wait Event 附录为准。
| wait_event_type | wait_event | 含义 |
|---|---|---|
Lock | transactionid | 等待某个事务提交或回滚(典型:等行锁持有者) |
Lock | tuple | 等待某个元组上的锁(并发更新同一行) |
Lock | relation | 等待表级锁 |
Lock | extend | 等待扩展关系文件的锁 |
Lock | virtualxid | 等待虚拟事务 ID 锁 |
Lock | advisory | 等待顾问锁 |
LWLock | XidGenLock | 等待事务 ID 分配器 |
LWLock | ProcArrayLock | 等待进程数组(快照获取、事务结束时排队) |
LWLock | buffer_content | 等待缓冲页内行数据的轻量锁 |
LWLock | buffer_mapping | 等待缓冲区映射表 |
Client | ClientRead | 等待客户端发来数据 |
Client | ClientWrite | 等待能把结果写给客户端 |
IO | DataFileRead | 读数据文件 |
IO | DataFileWrite | 写数据文件 |
IO | WALWrite | 写 WAL |
IO | WALSync | WAL 落盘同步(fsync) |
IPC | ParallelFinish | 等待并行 worker 全部结束 |
IPC | MessageQueueInternal | 等待并行消息队列 |
Timeout | PgSleep | pg_sleep() 休眠 |
Timeout | VacuumDelay | autovacuum 按成本延迟休眠 |
BufferPin | BufferPin | 等待缓冲区 pin 释放 |
时间戳字段怎么用
四个时间戳刻画了连接生命周期的不同节点,用 now() 与它们做差是最常用的量化手段:
SELECT pid,
now() - backend_start AS connection_age, -- 连接建立了多久
now() - xact_start AS transaction_age, -- 当前事务开了多久
now() - query_start AS query_age, -- 当前(或最近一条)SQL 跑了多久
now() - state_change AS idle_age, -- 空闲了多久
now() - state_change AS since_state_change
FROM pg_stat_activity
WHERE backend_type = 'client backend';- 排查慢 SQL:看
active会话的now() - query_start。 - 排查长事务:看所有会话的
now() - xact_start,注意idle in transaction也要算。 - 排查连接泄漏:
idle会话的now() - state_change巨大且数量持续增长,多半是应用没还连接。 state_change与query_start的区别容易混淆:query_start记录的是「最近一条 SQL 的开始时刻」,state_change记录的是「状态切换时刻」。对 idle 会话,query_start是它最后执行的那条 SQL 的开始时间,state_change是它执行完进入 idle 的时间,两者相减约等于最后那条 SQL 的耗时。
backend_type:视图里不只有业务连接
backend_type 区分了进程角色,常见的取值包括:
| 取值 | 说明 |
|---|---|
client backend | 普通客户端会话(业务连接) |
autovacuum launcher / autovacuum worker | 自动清理的调度进程与工作进程 |
walsender | 流复制/逻辑复制的发送进程 |
background writer / checkpointer / walwriter | 常驻辅助进程 |
parallel worker | 并行查询的工作进程(配合 leader_pid 定位所属会话) |
logical replication launcher / logical replication worker | 逻辑复制相关进程 |
做连接数巡检时,务必用 backend_type = 'client backend' 过滤出真正的业务连接,否则统计会被辅助进程污染。反过来,排查 autovacuum 行为时,就专门看 autovacuum worker 那几行的 query 列(内容形如 autovacuum: VACUUM public.xxx)。
权限与可见性
- 超级用户与被授予
pg_read_all_stats角色的用户可以看到所有行的全部字段。 - 普通用户虽然能看到所有行,但他人会话的
query、query_id、backend_xid、backend_xmin、client_addr等敏感字段会被置空,只能看到自己会话的完整信息。 application_name是客户端自报的字符串,可以伪造,安全审计时不要把它当作可信依据;可信的连接来源是client_addr(结合认证日志核对)。
-- 授予监控账号查看全量统计的权限
GRANT pg_read_all_stats TO monitor_user;终止会话:pg_cancel_backend 与 pg_terminate_backend
看清楚了要动手,用到两个内置函数:
| 函数 | 作用 | 相当于 | 影响范围 |
|---|---|---|---|
pg_cancel_backend(pid) | 取消该会话当前正在执行的一条 SQL | 客户端收到 SIGINT | 仅当前语句回滚,连接保留,事务未结束 |
pg_terminate_backend(pid) | 终止整个会话 | 客户端收到 SIGTERM | 连接断开,未提交事务回滚 |
-- 温和手段:先取消正在跑的语句
SELECT pg_cancel_backend(3128);
-- 强制手段:直接断开该会话
SELECT pg_terminate_backend(3128);
-- PG14+ 支持带超时的终止:最多等 5000ms 让会话自行退出
SELECT pg_terminate_backend(3128, 5000);使用顺序建议:先 cancel、后 terminate。cancel 只影响当前语句,对业务冲击小;但它对 idle in transaction 会话无效(没有正在执行的语句可取消),这种会话只能 terminate。kill 之后客户端会收到连接错误,应用需要能重连——这也是为什么强调应用侧要有健壮的连接池。
相关超时参数:让系统自动兜底
pg_stat_activity 里的问题状态,多数可以靠超时参数提前拦截,避免人工盯梢:
| 参数 | 拦截什么 | 作用范围 |
|---|---|---|
statement_timeout | 单条 SQL 执行超时 | 会话/角色/事务级设置 |
lock_timeout | 获取锁的等待超时 | 会话/角色/事务级设置 |
idle_in_transaction_session_timeout | 事务打开后空闲超过多久 | 会话/角色/实例级设置 |
idle_session_timeout | 整个会话空闲超过多久(PG14+) | 会话/角色级设置 |
-- 例:给报表角色设置 10 分钟语句超时与 5 分钟空闲事务超时
ALTER ROLE report_user SET statement_timeout = '10min';
ALTER ROLE report_user SET idle_in_transaction_session_timeout = '5min';与这些参数相关的锁等待语义,见本篇稍后的锁监控章节以及《锁等待与超时》一页。
版本演进速览
pg_stat_activity 的字段比较稳定,但有几个版本节点值得知道:
- PG13:新增
leader_pid,并行查询的 worker 可以关联回 leader 会话。 - PG14:新增
query_id(依赖内核参数compute_query_id);新增idle_session_timeout参数。 - PG16 及以后:等待事件清单持续扩充(如逻辑复制 apply 相关的
applytransaction等锁事件),backend_type取值也随新后台进程增加。 - 更早版本(PG10 以前)的
wait_event机制尚不存在,老库排障只能靠state+pg_locks猜。
快照语义:统计视图的事务级缓存
pg_stat_activity 虽然号称「实时」,但它的读取有一个容易忽略的细节:统计视图的数据在事务内会被缓存成快照。同一个事务里第一次访问任何统计视图时,服务器会取一份完整的统计快照并缓存;事务结束前,后续对统计视图的读取都返回同一份缓存——即使这期间其他会话的连接与状态已经发生了变化。
BEGIN;
-- 第一次访问统计视图,此刻建立快照
SELECT count(*) FROM pg_stat_activity WHERE state = 'active';
-- ……期间其他会话有新连接、有断开、有新查询……
-- 第二次读取:返回的是同一份快照,数字不变
SELECT count(*) FROM pg_stat_activity WHERE state = 'active';
-- 主动丢弃快照后再读,才能看到新状态
SELECT pg_stat_clear_snapshot();
SELECT count(*) FROM pg_stat_activity WHERE state = 'active';
COMMIT;这对写监控脚本的人有三个直接影响:
- 巡检脚本用自动提交模式。 psql 默认每条语句一个事务,每条查询都能拿到新快照;把多条统计查询包进同一个
BEGIN ... COMMIT反而会让后面的查询读到旧数据。 - 长事务里的统计查询会「失明」。 一个开了几个小时的事务若中途访问过统计视图,之后读到的都是几小时前的快照;在长事务内做监控判断尤其危险。
- 需要事务内多次读取时,调用
pg_stat_clear_snapshot()。 开销只是重新取一份快照,在监控循环里按需调用即可。
采样落库:为实时视图补上时间维度
实时快照回答「现在」,回答不了「过去 30 分钟连接数怎么变化的」。标准做法是周期性把聚合结果写入自建历史表:
-- 历史表(一次性创建)
CREATE TABLE pg_activity_history (
captured_at timestamptz NOT NULL DEFAULT now(),
datname text,
usename text,
application_name text,
state text,
wait_event_type text,
sessions integer NOT NULL
);
-- 每分钟执行一次的采样语句
INSERT INTO pg_activity_history
(datname, usename, application_name, state, wait_event_type, sessions)
SELECT datname, usename, application_name, state, wait_event_type, count(*)
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY datname, usename, application_name, state, wait_event_type;
-- 历史表只保留 14 天
DELETE FROM pg_activity_history
WHERE captured_at < now() - interval '14 days';有了历史表,「昨晚 23 点连接为什么暴涨」就变成一条普通的聚合查询。更完整的历史分析(SQL 级)由 pg_stat_statements 承担;会话级的过程证据还可以配合本章日志部分的时间线互相印证。
排障 SOP:从现象到字段
把视图字段与常见故障现象对应起来,是本页最重要的「速查资产」:
| 现象 | 第一眼看什么 | 判断依据 | 典型动作 |
|---|---|---|---|
| CPU 打满 | state = 'active' 且等待事件为空的会话 | 等待事件为空 = 纯 CPU 运算 | 找出最长 query,EXPLAIN 优化 |
| 连接数暴涨 | 按 application_name / client_addr 分组计数 | 某来源异常集中 | 定位应用,必要时 terminate 空闲连接 |
| SQL 卡住 | wait_event_type = 'Lock' | 在等锁而非在算 | pg_blocking_pids() 追阻塞链 |
| 事务长期不提交 | idle in transaction + xact_start | 空闲事务挂起 | 通知应用修复;超时 terminate |
| 复制发送异常 | backend_type = 'walsender' 的等待事件 | WALWrite/WALSync 堆积或无 walsender 行 | 查网络与磁盘(配合复制监控页) |
| 磁盘 IO 饱和 | wait_event_type = 'IO' 的分布 | DataFileRead 集中 | 查命中率与缺失索引 |
值班时按这张表「对号入座」,大多数场景两三步内就能把问题分流到具体专题页。
后台进程行同样值得看
backend_type 的过滤别只用来「排除」后台进程,它们本身就是观测点:
walsender行:流复制的发送进程。它的state长期active、等待事件集中在WALWrite/WALSync时,说明发送侧有压力;一行都没有则说明复制已断开(详见《pg_stat_replication 监控》)。autovacuum worker行:query列显示它正在清理哪张表(形如autovacuum: VACUUM public.orders)。巡检时确认「热点表是否有 worker 在光顾」,与pg_stat_user_tables.last_autovacuum互相印证。parallel worker行:配合leader_pid可以统计每个并行查询占用了多少额外进程,评估max_parallel_workers是否吃紧。checkpointer/background writer行:它们几乎总在Activity类等待中休眠;出现在IO等待且持续很久时,值得结合检查点统计排查(见《pg_stat_database 与 pg_stat_user_tables》页)。
实战示例
示例一:日常连接巡检
生产环境最常用的「一眼看全局」查询,按状态分组汇总业务连接:
SELECT datname,
usename,
application_name,
state,
count(*) AS connections
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY datname, usename, application_name, state
ORDER BY connections DESC; datname | usename | application_name | state | connections
---------+---------+------------------+---------------------+-------------
appdb | app | myapp-pool-1 | idle | 42
appdb | app | myapp-pool-2 | idle | 38
appdb | app | myapp-pool-1 | active | 9
appdb | app | report-tool | idle in transaction | 2
postgres| postgres| psql | active | 1
(5 rows)idle 数量远大于 active 说明连接池配大了;出现 idle in transaction 则要立刻定位那条会话。
示例二:长事务与空闲事务排查
SELECT pid,
datname,
usename,
state,
now() - xact_start AS xact_age,
now() - state_change AS idle_age,
backend_xid,
backend_xmin,
left(query, 60) AS last_query
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
AND now() - xact_start > interval '5 minutes'
ORDER BY xact_start;对超过阈值的 idle in transaction 会话,处理动作:
-- 先看清楚是什么(application_name、client_addr、query 都确认一遍)
SELECT pid, usename, application_name, client_addr, xact_start, query
FROM pg_stat_activity
WHERE state IN ('idle in transaction', 'idle in transaction (aborted)');
-- 确认无业务风险后终止
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle in transaction'
AND now() - state_change > interval '15 minutes';示例三:等待事件分布——判断瓶颈在 CPU、锁还是 IO
SELECT wait_event_type,
wait_event,
count(*) AS sessions
FROM pg_stat_activity
WHERE state = 'active'
AND backend_type = 'client backend'
GROUP BY wait_event_type, wait_event
ORDER BY sessions DESC; wait_event_type | wait_event | sessions
-----------------+----------------+----------
| | 6 <- 纯 CPU 运算,无等待
Lock | transactionid | 4 <- 行锁等待,去看 pg_locks
IO | DataFileRead | 3 <- 磁盘读,检查缓存/索引
Client | ClientRead | 2 <- 客户端收数据慢
(4 rows)读法:等待事件为空且 active 多,瓶颈在 CPU;Lock 多,去查阻塞链;IO 多,看命中率与磁盘;Client 多,问题可能在应用侧。
示例四:定位并行查询与它的 worker
-- leader 与其 worker 的对应关系
SELECT pid,
leader_pid,
backend_type,
state,
left(query, 50) AS query_head
FROM pg_stat_activity
WHERE query_id IS NOT NULL
AND backend_type IN ('client backend', 'parallel worker');worker 行的 leader_pid 即发起查询的会话;要终止整个并行查询,kill leader 即可,worker 会随之退出。
示例五:连接数逼近上限的应急处理
-- 当前连接数与上限
SELECT count(*) AS current_connections,
current_setting('max_connections') AS max_connections
FROM pg_stat_activity;
-- 找出连接数最多的客户端来源
SELECT client_addr, application_name, usename, count(*) AS n
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY client_addr, application_name, usename
ORDER BY n DESC;
-- 应急:批量断开某台失控机器的空闲连接(保留正在执行的)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE client_addr = '10.0.0.11'
AND state = 'idle'
AND application_name = 'myapp-pool-1';长期解法是接入连接池,见《连接池配置(PgBouncer)》一页。
示例六:连接泄漏的追踪闭环
现象:连接使用率每周稳步上涨,重启应用后回落——典型的连接泄漏。追踪流程三步:
-- 1. 确认泄漏画像:按来源分组看空闲连接的「年龄」
SELECT client_addr,
application_name,
usename,
count(*) AS idle_conns,
max(now() - state_change) AS oldest_idle
FROM pg_stat_activity
WHERE state = 'idle'
AND backend_type = 'client backend'
GROUP BY 1, 2, 3
HAVING count(*) > 10
ORDER BY oldest_idle DESC; client_addr | application_name | usename | idle_conns | oldest_idle
-------------+------------------+---------+------------+--------------
10.0.0.11 | myapp-pool-1 | app | 46 | 3 days 04:12
(1 row)-- 2. 保留现场证据(写入采样历史表,便于与发布时间对照)
INSERT INTO pg_activity_history
(datname, usename, application_name, state, wait_event_type, sessions)
SELECT datname, usename, application_name, state, wait_event_type, 1
FROM pg_stat_activity
WHERE client_addr = '10.0.0.11' AND state = 'idle';
-- 3. 应急释放:只断开长时间空闲的连接(active 连接不受影响)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE client_addr = '10.0.0.11'
AND state = 'idle'
AND now() - state_change > interval '1 hour';闭环动作:把第 1 步的查询接入每日巡检,oldest_idle 超过一天即告警;反馈应用团队检查连接归还逻辑。连接数的长期治理方案见《连接池配置(PgBouncer)》。
注意事项
不要拿 application_name 做安全判断。 它是客户端连接串里的一个任意字符串,伪造零成本。审计归审计(client_addr + 认证日志),业务归业务(用 application_name 分组统计)。
pg_stat_activity 是集簇级视图。 连在哪个库上查询都能看到全部会话;反过来,想让监控只针对某个库,必须显式 WHERE datname = ...。忘记过滤是「监控数字比预期大一圈」的常见原因。
统计开关被关闭时视图会失真。 track_activities = off 时,query、等待事件等字段不再更新,所有会话 state 显示 disabled。排障前先确认这个开关没有被误关。
pg_cancel_backend 对空闲事务无效。 没有「当前语句」可取消。要处理 idle in transaction 只能 pg_terminate_backend。另外 cancel 只是请求,极端情况下(比如被 C 语言层面的无限循环卡住)可能不生效,届时仍需 terminate。
kill autovacuum worker 要三思。 用 pg_terminate_backend 杀掉 autovacuum worker 会中断该表的清理进度(已做的工作不白费,但重做有成本)。反复手工杀 autovacuum 会让表膨胀越积越深,正确做法是调整 autovacuum 参数或优化大事务。
视图查询本身也会出现在视图里。 你自己执行的那条查询 SELECT ... FROM pg_stat_activity 也会有一行,pid 等于 pg_backend_pid()。写巡检脚本时记得排除自己,避免统计口径被污染。
时间差计算注意时区与时钟。 now() 返回的是事务开始时间而非当前时刻——长事务里反复调用 now() 得到的是同一个值。在会话内做时间差计算,请用 clock_timestamp()。
query 文本可能被截断与脱敏。 视图里的 query 有长度上限(超长语句会截断),且对无权限用户直接置空。需要完整 SQL 时应结合日志系统(本章节日志部分)而不是单纯依赖该视图。
最佳实践
- 把「连接巡检 SQL」固化成脚本或监控项。 按状态分组的连接数、最老事务的
xact_age、idle in transaction数量,这三个指标每天都要有人(或系统)看一眼,任何异常增长都值得追查。 - 用
backend_type = 'client backend'圈定业务连接。 所有连接数指标、连接池容量规划都应基于业务连接口径,而不是视图总行数。 - 为每个应用约定独立的
application_name。 排障时能一眼分辨连接来自哪个服务、哪个连接池;配合连接池时尤其重要(否则池里连接张冠李戴)。 - 给关键角色预置超时参数兜底。 报表、ETL 等低信任角色设置
statement_timeout与idle_in_transaction_session_timeout,让系统自动回收失控会话,而不是等人工发现。 - 把
idle in transaction当作告警级事件。 任何超过数分钟的空闲事务都应该触发告警:它同时引发锁等待、表膨胀、复制延迟三重问题。 - 排障顺序:state → wait_event → 时间差 → query。 先看状态分组找异常群体,再用等待事件判断瓶颈类型,再用时间戳量化严重程度,最后才逐条看 SQL。不要一上来就逐行读
query文本。 - 先 cancel 后 terminate,并留操作记录。 终止会话是侵入性操作,记录 pid、SQL、时间与批准人;对 autovacuum、流复制等系统进程更是如此。
- 授予监控账号
pg_read_all_stats而不是超级用户。 监控只需要读统计的权限;把超级用户交给监控脚本是常见的安全坏习惯。 - 升级前核对版本差异。
query_id(PG14+)、leader_pid(PG13+)等字段并非所有版本都有;写跨版本通用的巡检 SQL 时用pg_get_viewdef或查询information_schema.columns做能力探测。 - 实时快照留不住,历史数据要自己采。 对
pg_stat_activity做周期采样(比如每 10 秒落一张表),才能回答「昨晚 11 点连接为什么暴涨」这类事后问题;更长期的 SQL 级统计交给下一章的pg_stat_statements。 - 把等待事件纳入长期监控维度。
wait_event_type分布是免费的「瓶颈画像」,按小时聚合后能直观看到系统从 IO 型到锁型瓶颈的迁移趋势。
下一节
继续学习:pg_locks 锁监控
参考链接
- PostgreSQL 官方文档:pg_stat_activity
- PostgreSQL 官方文档:服务器信号函数(pg_cancel_backend / pg_terminate_backend)
- PostgreSQL 官方文档:Statement Behavior(statement_timeout / lock_timeout 等)
- PostgreSQL 官方文档:连接与认证配置
- PostgreSQL 官方文档:会话信息函数
- PostgreSQL 官方文档:Run-time Statistics(track_activities 等统计开关)
- PostgreSQL 官方文档:pg_locks 锁监控视图
- PostgreSQL 官方文档:预定义角色(pg_read_all_stats 等)