Jesse Soyland:一个PID锁住所有:在Postgres中定位锁的源头

Jesse Soyland:一个PID锁住所有:在Postgres中定位锁的源头

💡 原文英文,约2500词,阅读约需9分钟。
📝

内容提要

排查Postgres锁问题:先在pg_stat_activity中查找wait_event非空的等待进程,再用pg_locks定位持锁进程并追溯其操作。可通过COMMIT、ROLLBACK、pg_cancel_backend或pg_terminate_backend解除阻塞。常见锁源有ALTER TABLE、ORM事务、非并发建索引和VACUUM FULL。建议开启log_lock_waits并设置lock_timeout以预防。

🔎

延伸解读

锁等待的识别:从等待进程入手

排查Postgres锁问题,首先应关注pg_stat_activity中state为active且wait_event或wait_event_type非空的进程。这些进程正在等待锁资源。文章建议多次运行查询以排除短暂等待,并记录受影响PID。通过xact_time和query_time可判断事务和查询的持续时间,帮助定位长时间运行的操作。

追溯锁的源头:pg_locks与pg_stat_activity联动

找到等待进程后,用pg_locks查询其未授予的锁,获取锁类型和对象OID。再以对象OID查询已授予的锁,找到持锁进程PID。最后回到pg_stat_activity查看该进程的状态和最后执行的查询。文章示例中,一个idle in transaction的进程持有RowExclusiveLock,阻塞了ALTER TABLE。

解除阻塞:提交、回滚或终止进程

确认阻塞进程后,若其处于idle in transaction,可尝试COMMIT或ROLLBACK释放锁。若无法直接操作,可使用pg_cancel_backend取消查询,或pg_terminate_backend终止连接。文章提醒,终止连接是最后手段,可能中断事务。操作前应评估影响,避免误杀关键进程。

预防锁问题:日志与超时设置

为提前发现锁竞争,可开启log_lock_waits,记录等待锁的查询。文章指出该设置开销极低,适合生产环境。同时建议设置lock_timeout,让事务在等待超时后自动取消并释放锁,防止连锁阻塞。例如ALTER SYSTEM SET lock_timeout = '10s'。这些措施有助于减少锁问题对生产的影响。

❓

Q&A

如何快速找到Postgres中正在等待锁的进程?

查询pg_stat_activity视图,筛选state不为'idle'且wait_event或wait_event_type非NULL的进程。这些进程表示正在等待锁。

在Postgres中,如何找到持有锁并阻塞其他进程的源头PID?

先通过pg_locks查询等待进程的未授予锁,获取锁对象(如relation OID),再用该对象查询pg_locks中已授予的锁,找到持有锁的PID。

有哪些方法可以解除Postgres中的锁阻塞?

可以COMMIT或ROLLBACK持有锁的事务,或者使用SELECT pg_cancel_backend(PID)取消查询,若无效则用SELECT pg_terminate_backend(PID)终止后端进程。

Postgres中常见的导致锁问题的操作有哪些?

常见操作包括:ALTER TABLE(获取ACCESS EXCLUSIVE锁)、ORM框架产生的事务、非并发的CREATE INDEX、VACUUM FULL。

如何预防Postgres锁问题?

可以开启log_lock_waits记录锁等待,并设置lock_timeout(如ALTER SYSTEM SET lock_timeout = '10s')让事务超时后自动取消并释放锁。

Postgres中共享锁和排他锁有什么区别?

共享锁允许同一资源被多个会话同时访问;排他锁则只允许一个会话访问,通常会导致阻塞。

🏷️

标签

➡️

继续阅读