PostgreSQL pg_terminate_backend() 函数用法与实例

在 PostgreSQL 运维与高并发业务场景中,我们经常会遇到长事务阻塞、查询慢死锁、或是执行 DROP DATABASE / ALTER DATABASE RENAME 时因存在活动连接而报错的情况。

pg_terminate_backend() 是 PostgreSQL 提供的核心系统管理函数,用于向指定的后台会话进程发送终止信号(SIGTERM),强制断开客户端连接并释放其持有的所有锁和资源。

语法

pg_terminate_backend() 函数的调用签名如下:

pg_terminate_backend(pid integer, timeout bigint DEFAULT 0) -> boolean

参数

pid
必需。要终止的后台进程标识符(Process ID,整数类型),通常从系统视图 pg_stat_activity 的 pid 列获取。
timeout
可选(PostgreSQL 14+ 引入)。等待进程实际终止的超时毫秒数,默认值为 0。
  • 当为 0 时,函数在向操作系统进程发送终止信号后立即返回 true,不阻塞等待;
  • 当大于 0 时,函数将持续等待进程完全退出,直到指定毫秒数;如果在超时前成功退出返回 true,超时仍未退出则返回 false。

返回值

  • 返回 true:表示终止信号发送成功(或在指定超时时间内进程成功退出);
  • 返回 false:表示目标 PID 不存在、超时未退出或当前用户无权终止该进程。

权限要求

  • 超级用户(Superuser) 或具有 pg_signal_backend 预定义角色的用户可以终止任意用户的后台连接;
  • 普通用户 仅允许终止属于其自身角色的连接,尝试终止其他用户的进程将返回 false 并记录权限警告。

与 pg_cancel_backend() 的区别

PostgreSQL 提供了两个会话控制函数,很多开发者容易混淆:

特性 pg_cancel_backend(pid) pg_terminate_backend(pid)
底层信号 发送 SIGINT 信号 发送 SIGTERM 信号
动作影响 仅取消该连接当前正在执行的一条 SQL 查询 彻底销毁整个连接会话,客户端直接掉线
客户端状态 连接依然保持,可继续执行后续查询 客户端抛出连接断开错误(如 connection closed)
适用场景 终止单条卡住的超长慢查询,不影响客户端应用连接池 强制清理无响应进程、死锁、准备删除数据库清空连接

实战示例

查询当前所有活动连接与 PID

在终止进程前,必须先通过 pg_stat_activity 找到目标连接的 pid、状态及正在执行的 SQL:

SELECT
  pid,
  usename,
  datname,
  client_addr,
  state,
  now() - query_start AS duration,
  query
FROM pg_stat_activity
WHERE pid <> pg_backend_pid()
ORDER BY duration DESC;

[!TIP] WHERE pid <> pg_backend_pid() 用于排除当前正在执行查询的自身连接,避免误杀当前会话。

示例 1:终止单个指定的连接进程

假设发现 pid = 41285 的会话处于异常挂起状态,直接终止:

SELECT pg_terminate_backend(41285);

输出:

 pg_terminate_backend 
----------------------
 t
(1 row)

输出 t(true)表示信号成功发送,目标连接已断开。

示例 2:删除数据库前清空目标库的所有活动连接

在执行 DROP DATABASE testdb; 时,若有任何客户端连接到 testdb,PostgreSQL 会报 ERROR: database "testdb" is being accessed by other users。

可以使用如下语句批量强杀该数据库的所有非自身连接:

SELECT
  pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = 'testdb'
  AND pid <> pg_backend_pid();

执行后即可顺利执行 DROP DATABASE testdb;。

示例 3:批量清理空闲过久的事务连接(idle in transaction)

在业务系统中,“开启事务后未提交亦未回滚”的连接(idle in transaction)会长时间阻止表上的元数据锁释放或导致 VACUUM 无法清理死元组。

以下 SQL 可安全清理事务开启超过 10 分钟的僵尸连接:

SELECT
  pid,
  usename,
  now() - state_change AS idle_duration,
  pg_terminate_backend(pid) AS terminated
FROM pg_stat_activity
WHERE state = 'idle in transaction'
  AND state_change < now() - INTERVAL '10 minutes';

结论

pg_terminate_backend() 是 PostgreSQL 数据库管理员手中强有力的运维工具。在遇到业务死锁、高危全表扫描卡死、或需要释放目标库连接时,配合 pg_stat_activity 过滤条件,能够精准高效地恢复数据库健康状态。在日常使用中,建议先尝试温和的 pg_cancel_backend(),无效时再使用 pg_terminate_backend()。