pg_stat_activity是云原生数据仓库AnalyticDB PostgreSQL版用来定位实例当前执行查询的系统视图,每行显示一个服务器进程同时详细描述与之关联的用户会话和查询,可以有效帮助用户分析排查当前运行的SQL任务以及异常问题。

注意事项

只有superuser用户或者是正在报告的进程的拥有者时,才可以使用pg_stat_activity视图。

pg_stat_activity视图的字段描述

字段 类型 描述
datid oid 数据库OID。
datname name 数据库名称。
procpid integer 后端进程的进程ID。
说明 只有4.3版本支持procpid字段。
pid integer 后端进程的进程ID。
说明 只有6.0版本支持pid字段。
sess_id integer 会话ID。
usesysid oid 用户OID。
usename name 用户名。
current_query text 当前正在执行的查询。默认情况下,查询文本最多显示1024个字符,超出部分会被截断,如需显示更多字符,可以通过参数track_activity_query_size配置。
说明 只有4.3版本支持current_query字段。
query text 最近查询的文本。如果stateactive,显示当前正在执行的查询。在其他状态下,显示上一个执行的查询。 默认情况下,查询文本最多显示1024个字符,超出部分会被截断,如需显示更多字符,可以通过参数track_activity_query_size配置。
说明 只有6.0版本支持query字段。
waiting boolean 如果当前SQL在锁等待,值为True,否则为False。
query_start datetime 当前活动查询开始执行的时间。如果state不是active,显示上一个查询的开始时间。
backend_start datetime 当前后端进程的开始时间。
backend_xid xid 后端进程当前的事务ID。
backend_xmin xid 后端的xmin范围。
client_addr inet 客户端的IP地址。如果client_addr为空,表示客户端通过服务器上的Unix套接字连接,或者表示进程是内部进程(例如AUTOVACUUM)。
client_port integer 客户端和后端通信的TCP端口号。如果使用Unix套接字,值为-1。
client_hostname text 客户端主机名,通过client_addr的反向DNS查找报告。
application_name text 客户端应用名。
xact_start timestamptz 当前事务的启动时间。如果没有活动事务,值为空。如果当前查询是第一个事务,值与query_start的值相同。
waiting_reason text 当前执行等待的原因,可能是等锁或者等待节点间数据的复制。
state text 后端的当前状态,取值范围:active,idle,idle in transaction,idle in transaction (aborted),fastpath function call,disabled。
说明 只有6.0版本支持state字段。
state_change timestampz 上次state状态切换的时间。
说明 只有6.0版本支持state_change字段。
rsgid oid 资源组OID。
rsgname text 资源组名称。
rsgqueueduration interval 对于排队查询,查询排队的总时间。

查看连接信息

通过下述SQL确认当前的连接用户和对应的连接机器。

SELECT datname,usename,client_addr,client_port FROM pg_stat_activity ;
datname  |  usename  |  client_addr   | client_port
---------+----------+---------------+-------------
postgres | joe       |  xx.xx.xx.xx   |       60621
postgres | gpmon     |  xx.xx.xx.xx   |       60312
(9 rows)

查看SQL运行信息

获取当前用户执行SQL信息:

6.0版本:

SELECT datname,usename,query FROM pg_stat_activity ;
 datname  | usename  |                        query
----------+---------+--------------------------------------------------------------
 postgres | postgres | SELECT datname,usename,query FROM pg_stat_activity ;
 postgres | joe      | 
(2 rows)

4.3版本:

SELECT datname,usename,current_query FROM pg_stat_activity ;
 datname  | usename  |                        current_query
----------+---------+--------------------------------------------------------------
 postgres | postgres | SELECT datname,usename,current_query FROM pg_stat_activity ;
 postgres | joe      | <IDLE>
(2 rows)

获取当前正在运行的SQL信息:

6.0版本:

SELECT datname,usename,query
   FROM pg_stat_activity
   WHERE state != 'idle' ;

4.3版本:

SELECT datname,usename,current_query
   FROM pg_stat_activity
   WHERE current_query != '<IDLE>' ;

查看耗时较长的查询

查看当前运行中的耗时较长的SQL语句:

6.0版本:

select current_timestamp - query_start as runtime, datname, usename, query
    from pg_stat_activity
    where state != 'idle'
    order by 1 desc;

4.3版本:

select current_timestamp - query_start as runtime, datname, usename, current_query
    from pg_stat_activity
    where current_query != '<IDLE>'
    order by 1 desc;

返回示例如下:

runtime         |    datname     | usename  |                                current_query
----------------+---------------+---------+------------------------------------------------------------------------------
 00:00:34.248426 | tpch_1000x_col | postgres | select
                                             :         l_returnflag,
                                             :         l_linestatus,
                                             :         sum(l_quantity) as sum_qty,
                                             :         sum(l_extendedprice) as sum_base_price,
                                             :         sum(l_extendedprice * (1 - l_discount)) as sum_disc_price,
                                             :         sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge,
                                             :         avg(l_quantity) as avg_qty,
                                             :         avg(l_extendedprice) as avg_price,
                                             :         avg(l_discount) as avg_disc,
                                             :         count(*) as count_order
                                             : from
                                             :         public.lineitem
                                             : where
                                             :         l_shipdate <= date '1998-12-01' - interval '93' day
                                             : group by
                                             :         l_returnflag,
                                             :         l_linestatus
                                             : order by
                                             :         l_returnflag,
                                             :         l_linestatus;
 00:00:00        | postgres       | postgres | select
                                             :        current_timestamp - query_start as runtime,
                                             :        datname,
                                             :        usename,
                                             :        current_query
                                             :     from pg_stat_activity
                                             :     where current_query != '<IDLE>'
                                             :     order by 1 desc;
(2 rows)

可以看到第一个查询耗时较久,已经运行了34s还没有结束。

异常SQL诊断及修复

如果一个SQL运行很长时间没有返回结果,需要检查该SQL还在运行中还是已经被Block:

6.0版本:

SELECT datname,usename,query
   FROM pg_stat_activity
   WHERE waiting;

4.3版本:

SELECT datname,usename,current_query
   FROM pg_stat_activity
   WHERE waiting;

需要注意的是上述返回结果只能获取当前因为Lock而被Block的SQL,无法获取因为其他原因被Block的SQL。绝大多数情况下SQL都是因为Lock而被Block,但也会存在其他情况,例如等待I/O、定时器等。如果上述SQL有返回结果,说明有SQL被Lock阻塞,需要进一步明确相互Block的SQL信息:

SELECT
        w.query as waiting_query,
        w.pid as w_pid,
        w.usename as w_user,
        l.query as locking_query,
        l.pid as l_pid,
        l.usename as l_user,
        t.schemaname || '.' || t.relname as tablename
    from pg_stat_activity w
    join pg_locks l1 on w.pid = l1.pid and not l1.granted
    join pg_locks l2 on l1.relation = l2.relation and l2.granted
    join pg_stat_activity l on l2.pid = l.pid
    join pg_stat_user_tables t on l1.relation = t.relid
    where w.waiting;

通过上述SQL的返回结果可以确认相互Block的SQL和对应的执行pid。在明确了SQL的阻塞信息后,可以通过Cancel或者Kill Query的方式进行恢复。通过Cancel取消一个正在运行的Query:

SELECT pg_cancel_backend(pid)

需要在一个运行Query的Session中执行,如果Session本身就是Idle的,执行不起作用。另外取消这个Query需要花费一定的时间来做清理和事务的回滚。使用pg_terminate_backend来清理Idle Session,也可以用来终止Query:

SELECT pg_terminate_backend(pid);

该用户的连接会断开。尽量避免在正在运行Query的进程pid上执行。需要注意的是文中提到操作需要用户有superuser的权限。