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 后,阻塞仍持续,大概率是其他后台进程或未清理的客户端状态在作祟。
实操建议:
- 检查
autovacuum进程:它可能因表膨胀卡在AccessExclusiveLock,且 PID 在pg_stat_activity中显示为autovacuum worker process;不能用pg_terminate_backend()直接杀(会触发重试),应临时设autovacuum = off或调大vacuum_cost_limit - prepared statement 未 DEALLOCATE:客户端断连后,prepared plan 可能长期驻留内存,占用锁资源;查
pg_prepared_statements,用DEALLOCATE ALL清理(需 superuser) - 应用层连接池(如 PgBouncer)配置了
pool_mode = transaction时,一个事务内多次BEGIN不会释放连接,容易积累锁;此时pg_terminate_backend()杀的是 pgbouncer 的连接,不是后端真正持锁的 session
生产环境慎用:kill 前务必确认事务一致性影响
直接 pg_terminate_backend() 是最快止血手段,但可能让业务处于中间状态:比如订单服务刚写完支付记录、还没更新库存,就被 kill,后续靠应用重试很难自愈。
实操建议:
- 优先用
SELECT pg_cancel_backend(pid)尝试优雅中断(只取消当前查询,保留事务上下文),再观察是否释放锁;仅当pg_cancel_backend()无效(如卡在 I/O 或锁等待)才升级为pg_terminate_backend() - 杀之前用
SELECT query, state, backend_start, xact_start, state_change FROM pg_stat_activity WHERE pid = ?;确认该会话是否在跑关键业务逻辑(如UPDATE orders SET status = 'paid'),而非只是空闲连接 - PostgreSQL 14+ 支持
pg_terminate_backend(pid, 'immediate')(第二个参数为字符串),但仅限 superuser,且仍不保证原子性;不要把它当成“万能强杀开关”
最麻烦的情况往往不是锁本身,而是 kill 后应用没处理好连接异常,反复重连重试,瞬间制造新一批阻塞。所以别只盯着数据库端,得同步看应用日志里有没有 Connection reset 或 server closed the connection unexpectedly。










