pg_blocking_pids() 返回空不等于无阻塞,需查 pg_stat_activity 中 blocked_pid 非空行及 pg_locks 中 granted=false 的锁;pg_terminate_backend() 需权限、活跃PID且慎用,优先尝试 pg_cancel_backend()。
pg_blocking_pids 返回空但查询明显卡住?先确认是否真被阻塞
很多情况下 pg_blocking_pids() 返回空数组,不是因为没阻塞,而是当前会话没被「直接阻塞」——比如它在等一个已提交事务释放的锁(而该事务早已结束),或锁被 cascade 删除、或锁被 pg_terminate_backend() 干掉后残留状态未清理。更可靠的方式是查 pg_locks 和 pg_stat_activity 关联:用 blocked_pid = pid 找出谁在等谁,再逆向定位源头。
实操建议:
- 运行
SELECT blocked_pid, blocking_pid FROM pg_stat_activity WHERE blocked_pid IS NOT NULL;—— 这比pg_blocking_pids()更直观,不依赖当前会话上下文 - 若
blocking_pid为 0,说明是“锁等待但无活跃 blocker”,常见于长事务未提交后突然崩溃,残留transactionid锁未释放,需查pg_locks中granted = false的行并关联virtualxid或transactionid -
pg_blocking_pids(pid)只对正在运行的pid有效;若目标会话已断开但连接未彻底回收(如客户端异常退出),pg_stat_activity.state = 'idle in transaction (aborted)'仍可能持有锁,此时pg_blocking_pids()不返回任何值
pg_terminate_backend 被拒绝执行?检查权限和连接状态
pg_terminate_backend() 不是所有用户都能调用,也不是所有 PID 都能被杀。常见失败原因不是语法错,而是权限或状态不匹配。
实操建议:
- 必须拥有
postgres角色,或被显式授予pg_signal_backend权限(PostgreSQL 10+);普通CREATEDB或SUPERUSER不自动包含此能力 - 传入的
pid必须存在于pg_stat_activity.pid中;若显示state = 'inactive'或已消失,函数返回false,不报错也不生效 - 若目标会话正执行 DDL(如
ALTER TABLE ... ADD COLUMN),PostgreSQL 会延迟终止直到语句完成(防止元数据损坏),表现为调用返回true但会话仍卡在active状态几秒到几十秒 - 避免用
pg_terminate_backend(0)或负数 PID —— 不报错但无效果,且可能掩盖真实问题
杀完还堵?警惕 autovacuum 和 prepared statement 残留
手动 kill 掉 blocker 后,阻塞仍持续,大概率是其他后台进程或未清理的客户端状态在作祟。