在CentOS系统中,PostgreSQL锁问题(如死锁、长事务阻塞)的解决需遵循“排查定位→处理解锁→预防优化”的流程。以下是具体步骤:
要解决锁问题,首先需明确被锁对象、阻塞进程及等待链。通过PostgreSQL系统视图可快速获取这些信息:
执行以下SQL查询,替换your_table为目标表名,可获取表的锁模式、持有锁的进程ID(PID)及当前执行的SQL语句:
SELECT
c.relname AS 表名,
l.mode AS 锁模式,
l.pid AS 进程ID,
a.query AS 阻塞语句,
a.state AS 状态
FROM
pg_locks l
JOIN
pg_class c ON l.relation = c.oid
LEFT JOIN
pg_stat_activity a ON l.pid = a.pid
WHERE
NOT l.granted AND -- 未授予的锁(即阻塞锁)
c.relkind = 'r' AND -- 只查询普通表(排除索引、视图等)
c.relname = 'your_table';
若需查看所有被锁对象,可省略c.relname = 'your_table'条件。
通过以下查询可明确谁阻塞了谁,快速识别阻塞源(即“根进程”):
SELECT
blocked.pid AS 被阻塞进程,
blocked.query AS 被阻塞语句,
blocking.pid AS 阻塞源进程,
blocking.query AS 阻塞源语句
FROM
pg_stat_activity blocked
JOIN
pg_locks l1 ON l1.pid = blocked.pid
JOIN
pg_locks l2 ON
l2.locktype = l1.locktype AND
l2.database IS NOT DISTINCT FROM l1.database AND
l2.relation IS NOT DISTINCT FROM l1.relation AND
l2.page IS NOT DISTINCT FROM l1.page AND
l2.tuple IS NOT DISTINCT FROM l1.tuple AND
l2.virtualxid IS NOT DISTINCT FROM l1.virtualxid AND
l2.transactionid IS NOT DISTINCT FROM l1.transactionid AND
l2.classid IS NOT DISTINCT FROM l1.classid AND
l2.objid IS NOT DISTINCT FROM l1.objid AND
l2.objsubid IS NOT DISTINCT FROM l1.objsubid AND
l2.pid != l1.pid -- 排除自身
JOIN
pg_stat_activity blocking ON blocking.pid = l2.pid;
结果中,阻塞源进程的PID是解决问题的关键。
某些锁类型易引发问题,需特别关注:
ALTER TABLE、DROP TABLE)的独占锁,会阻塞所有其他操作(包括读);UPDATE、DELETE语句持有的行级排他锁,可能升级为表级锁(如未使用索引的全表扫描)。定位到阻塞源后,可通过以下方式解决:
使用pg_terminate_backend()函数终止阻塞进程(需superuser权限):
SELECT pg_terminate_backend(<阻塞进程PID>);
例如,若阻塞进程PID为12345,则执行:
SELECT pg_terminate_backend(12345);
注意:pg_terminate_backend()会回滚该进程的事务,可能导致部分数据未提交。
若存在多个锁等待进程,可通过以下SQL批量生成终止语句(复制执行即可):
WITH deadlock_pids AS (
SELECT pid
FROM pg_stat_activity
WHERE wait_event_type = 'Lock' AND state = 'active'
)
SELECT pg_terminate_backend(pid)
FROM deadlock_pids;
此命令会终止所有处于active状态且等待锁的进程。
若不想终止整个进程,可使用pg_cancel_backend()取消特定查询(不会回滚事务):
SELECT pg_cancel_backend(<阻塞进程PID>);
适用于需要保留事务上下文的场景。
解决现有锁问题后,需通过以下措施预防再次发生:
ALTER TABLE与UPDATE分开执行);CREATE INDEX)和DML(如UPDATE),DDL会获取AccessExclusiveLock,易阻塞其他操作。WHERE条件列创建索引,避免全表扫描(全表扫描会将行级锁升级为表级锁);CREATE INDEX CONCURRENTLY创建索引,避免锁表(该命令不会阻塞读写操作,但需注意:不能在有活跃事务时执行,且需执行两次以确保索引一致性)。SET lock_timeout = '5s'设置单个查询的最长等待时间(超时报错,避免无限等待);BEGIN; SET LOCAL lock_timeout = '3s'; ...; COMMIT;限制事务内的锁等待时间;postgresql.conf中的deadlock_timeout(默认1秒),调整死锁检测频率(增大该值可减少误判,减小则加快检测速度)。若业务允许,将隔离级别从SERIALIZABLE(串行化,最严格)降低为READ COMMITTED(读已提交),减少间隙锁(Gap Lock)的使用,降低死锁概率。
通过以上步骤,可有效解决CentOS上PostgreSQL的锁问题,并预防其再次发生。需注意的是,预防优于解决,日常运维中应定期监控锁状态(如通过pg_stat_activity和pg_locks视图),及时发现潜在问题。