PostgreSQL 事务与并发系列 · 第三期
Clyde Jin
04-24
DBA
37

PostgreSQL 事务与并发系列 · 第三期

锁机制与死锁处理实战

前两期我们深入了 MVCC 与快照隔离,知道了“读不阻塞写”。但有些场景必须靠锁来强制串行化。本期将带你掌握 PostgreSQL 中各类锁的用法、冲突关系、死锁成因与排查,并提供生产环境可用的监控脚本。

一、为什么有了 MVCC 还需要锁?

MVCC 主要解决了 读写冲突(读不阻塞写,写不阻塞读),但它无法解决 写写冲突 以及某些需要“绝对最新数据”的场景。

例如:

  • 两个事务同时对同一行执行 UPDATE,必须有一个先等另一个完成,否则更新会相互覆盖。
  • 业务上要求“先查询余额,余额足够才扣款”,如果不用锁就可能出现超卖。

因此,PostgreSQL 在 MVCC 之上仍然保留了完善的锁机制,包括表级锁、行级锁,以及用于应用层协调的咨询锁。


二、表级锁

表级锁由 PostgreSQL 内核自动管理,但你可以通过 LOCK 命令显式指定。表级锁按照冲突程度从低到高排列如下:

锁模式 关键字 冲突对象 典型场景
ACCESS SHARE ACCESS SHARE 仅与 ACCESS EXCLUSIVE 冲突 SELECT 查询自动加此锁
ROW SHARE ROW SHARE EXCLUSIVEACCESS EXCLUSIVE 冲突 SELECT FOR UPDATE/SHARE
ROW EXCLUSIVE ROW EXCLUSIVE SHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE 冲突 UPDATEDELETEINSERT
SHARE UPDATE EXCLUSIVE SHARE UPDATE EXCLUSIVE SHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE 冲突 VACUUMCREATE INDEX CONCURRENTLY
SHARE SHARE ROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE 冲突 保护表结构不被修改,但允许读
SHARE ROW EXCLUSIVE SHARE ROW EXCLUSIVE ROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE 冲突 较少直接使用,类似保护整个表
EXCLUSIVE EXCLUSIVE ROW SHAREROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE 冲突 允许读但阻止所有写和并发锁
ACCESS EXCLUSIVE ACCESS EXCLUSIVE 与所有锁模式冲突 DROP TABLETRUNCATEREINDEXVACUUM FULLALTER TABLE

查看当前表级锁

SELECT relation::regclass AS table_name, 
       mode, 
       granted,
       pid
FROM pg_locks
WHERE locktype = 'relation'
  AND relation IS NOT NULL;

显式加锁示例

-- 防止其他事务对表进行 DDL 操作,但允许普通查询
LOCK TABLE my_table IN SHARE MODE;

-- 禁止任何并发写入,但允许读取
LOCK TABLE my_table IN EXCLUSIVE MODE;

-- 完全排他(用于维护)
LOCK TABLE my_table IN ACCESS EXCLUSIVE MODE;

三、行级锁

行级锁不会锁整个表,而是锁住特定的行版本。它们也是通过 pg_locks 查看,locktypetuple(旧版本)或 transactionid(行锁本质关联事务)。更直观的是通过 SELECT ... FOR UPDATE/SHARE 来加锁。

行锁模式 关键字 冲突(与另一个行锁) 说明
FOR KEY SHARE FOR KEY SHARE FOR UPDATEFOR NO KEY UPDATE 冲突,但与 FOR SHAREFOR KEY SHARE 兼容 只阻止键的更新(外键检查常用)
FOR SHARE FOR SHARE FOR UPDATEFOR NO KEY UPDATE 冲突,与 FOR SHAREFOR KEY SHARE 兼容 共享锁,防止被更新或删除
FOR NO KEY UPDATE FOR NO KEY UPDATE FOR UPDATE 冲突,与 FOR NO KEY UPDATEFOR SHAREFOR KEY SHARE 兼容 更新非键列时自动加此锁
FOR UPDATE FOR UPDATE 与所有其他行锁冲突 排他锁,禁止其他任何并发修改或加锁

典型用法

-- 锁定选中的行,防止其他事务更新/删除,直到事务结束
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- 此时可以安全地基于当前余额做逻辑
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;

-- 共享模式:只阻止更新或删除,但不阻止其他事务加共享锁
SELECT * FROM accounts WHERE id = 1 FOR SHARE;

SKIP LOCKED 与 NOWAIT

  • NOWAIT:无法立即获得锁时立即报错,而不等待。
  • SKIP LOCKED:跳过已经被其他事务锁定的行,只返回未锁定的行。非常适合任务队列(多个 worker 抢任务)。
-- 从任务表中取一条未锁定的任务
SELECT * FROM jobs WHERE status = 'pending'
ORDER BY created_at LIMIT 1
FOR UPDATE SKIP LOCKED;

四、咨询锁(Advisory Locks)

咨询锁是一种应用层逻辑锁,完全由应用程序负责获取和释放,不绑定具体的表或行。可用于跨进程协调、防止并发执行同一段业务逻辑。

-- 获取一个会话级别的咨询锁(整型键)
SELECT pg_advisory_lock(12345);

-- 尝试获取,如果拿不到就跳过
SELECT pg_try_advisory_lock(12345);

-- 释放
SELECT pg_advisory_unlock(12345);

-- 事务级别的咨询锁,随事务自动释放
SELECT pg_advisory_xact_lock(12345);

典型场景:定时任务防止重叠执行、消息队列去重、分布式计数等。


五、死锁:成因与检测

5.1 死锁示例

时间 事务 A 事务 B
T1 BEGIN; BEGIN;
T2 UPDATE accounts SET balance=balance-100 WHERE id=1; (获得 id=1 的行锁)
T3 UPDATE accounts SET balance=balance+100 WHERE id=2; (获得 id=2 的行锁)
T4 UPDATE accounts SET balance=balance+100 WHERE id=2; (等待事务 B 释放 id=2 的锁)
T5 UPDATE accounts SET balance=balance-100 WHERE id=1; (等待事务 A 释放 id=1 的锁)

此时互相等待 ⇒ 死锁。

5.2 PostgreSQL 的死锁检测

PostgreSQL 有一个后台进程 deadlock_timeout(默认 1 秒),每隔这么长时间检查一次锁等待图。如果发现循环依赖,就选择一个事务作为牺牲品,回滚它并释放锁,同时返回错误:

ERROR: deadlock detected

5.3 如何定位死锁原因

  • 查看 PostgreSQL 日志(设置 log_lock_waits = on,死锁会被详细记录)。
  • 查询当前锁等待情况:
-- 查看当前阻塞与被阻塞的会话
SELECT blocked_locks.pid     AS blocked_pid,
       blocked_activity.query AS blocked_query,
       blocking_locks.pid     AS blocking_pid,
       blocking_activity.query AS blocking_query
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype
  AND blocking_locks.relation = blocked_locks.relation
  AND blocking_locks.page = blocked_locks.page
  AND blocking_locks.tuple = blocked_locks.tuple
  AND blocking_locks.virtualxid = blocked_locks.virtualxid
  AND blocking_locks.transactionid = blocked_locks.transactionid
  AND blocking_locks.classid = blocked_locks.classid
  AND blocking_locks.objid = blocked_locks.objid
  AND blocking_locks.objsubid = blocked_locks.objsubid
  AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;

更简洁的视图(可考虑用 pg_blocking_pids(pid) 函数):

SELECT pid, 
       usename, 
       state, 
       query,
       pg_blocking_pids(pid) AS blocked_by
FROM pg_stat_activity
WHERE cardinality(pg_blocking_pids(pid)) > 0;

5.4 避免死锁的最佳实践

  1. 统一访问顺序:如果多个事务需要更新多张表或多行,按相同的顺序加锁(例如总是先锁 id=1 再锁 id=2)。
  2. 尽可能缩短事务:不要在事务中做耗时的业务逻辑或等待用户交互。
  3. 使用 NOWAIT:如果不能立即获得锁,就让事务快速失败并重试,而不是等待造成死锁风险。
  4. 适当使用 SKIP LOCKED:对于任务队列类应用,完全避免锁冲突。
  5. 索引设计:确保 WHERE 条件能准确命中索引,避免表级锁升级(例如缺失索引会导致行锁升级为表锁的误解?实际上 PostgreSQL 行锁不会自动升级,但是缺少索引会导致锁定更多行,增加死锁概率)。

六、综合排查脚本示例

6.1 找出持有行锁却长时间不提交的事务

SELECT pid, 
       now() - xact_start AS duration,
       state,
       query
FROM pg_stat_activity
WHERE pid IN (
    SELECT pid FROM pg_locks WHERE locktype = 'transactionid')
  AND state != 'idle'
ORDER BY duration DESC;

6.2 强制终止阻塞的事务

-- 先查看 pid
SELECT pid, query, state FROM pg_stat_activity WHERE state = 'active' AND ...;
-- 终止(会回滚事务)
SELECT pg_terminate_backend(pid);

6.3 监控死锁发生频率(从日志解析)

如果你启用了 log_lock_waits = on 并设置了 deadlock_timeout,可以从 PostgreSQL 日志(默认在 pg_log 目录)中搜索 deadlock detected


七、总结与下期预告

本期我们完整解读了 PostgreSQL 的锁体系:

  • 表级锁冲突矩阵与自动加锁行为
  • 行级锁(FOR UPDATE/SHARE)及其与 MVCC 的配合
  • 咨询锁的应用场景
  • 死锁的成因、检测机制和避免策略
  • 实用的锁监控和终止脚本

掌握了这些,你已经可以应对绝大多数并发冲突场景。

第四期预告:事务隔离级别深度测试与序列化异常

  • 深入分析 READ COMMITTEDREPEATABLE READSERIALIZABLE 的行为边界
  • 什么是序列化异常?PostgreSQL 如何通过 SSI(可串行化快照隔离)检测?
  • 实战再现:幻读、写偏斜(Write Skew)的复现与规避
  • 应用层如何选择合适的隔离级别

第四期将带你从理论走向实战调优,敬请期待!

有任何锁相关的疑难杂症(比如锁等待迟迟不释放、死锁频繁出现),欢迎留言区交流,我们下期见!

Star
Donate
PostgreSQL 事务与并发系列 · 第二期
Previous
PostgreSQL 事务与并发系列 · 第四期
Next

Leave a comment

Registration is not required

Clyde Jin
303 Articles
0 Comments
0 Like
Recent Posts

HiddenMerit Daily · Issue 60

📊 HiddenMerit Daily · Issue 60 Focus on Database Frontiers, Practical Insights for DBAs July 22, 2026 | 5 Selected Global Breaking News 01|Oracle Releases Largest Quarterly Patch in History: 1,449 Patches Fix 1,235 CVEs, 261 Critical On July 21, Oracle released its July 2026 Critical Patch Update (CPU), setting a record for the largest single patch release in the company’s history. This CPU contains 1,449 security patches fixing 1,235 independent CVEs across 32 Oracle product families, of which 261 patches are rated Critical. Patch Distribution by Product Family: Product Family Patches Remotely Exploitable Without Authentication Oracle E‑Business Suite 410 45 Oracle Fusion Middleware 355 219 Oracle Communications 168 122 Oracle MySQL 54 9 Oracle Database Server 15 6 Context for This Patch: Oracle had already issued an urgent “Prepare Now” warning a week earlier, emphasising that AI is fundamentally lowering the barrier to discovering and exploiting vulnerabilities – frontier AI models can analyse software changes, reverse‑engineer security patches, and develop attack paths at unprecedented speed. Oracle has collaborated with state‑of‑the‑art models from Anthropic and OpenAI to proactively identify and fix potential security vulnerabilities. Oracle strongly recommends that customers immediately test and deploy this update in their monthly patch cycles. […]

HiddenMerit Daily · Issue 59

📊 HiddenMerit Daily · Issue 59 Focus on Database Frontiers, Practical Insights for DBAs July 21, 2026 | 5 Selected Global Breaking News 01|Oracle Issues Urgent Warning: July 21 Release Update to Fix Large Number of High‑Risk Vulnerabilities, Immediate Deployment Recommended On July 13, Oracle issued an urgent security warning, strongly recommending that all customers running supported Oracle Database versions (including Oracle Database 19c and Oracle AI Database 26ai) immediately test and deploy the Release Update (RU) after its release on July 21. Background: New frontier AI models are significantly lowering the barrier to discovering and exploiting software vulnerabilities – these models can identify weaknesses, analyse software changes, reverse‑engineer security patches, and develop potential attack paths at unprecedented speed and scale. AI models are also becoming increasingly adept at combining multiple weaknesses across the application and data stack into complex attacks, even when individual weaknesses do not themselves pose a serious risk. As a result, protecting systems solely at the network or application layer is no longer sufficient; enterprises must protect the entire technology stack. Fixes Included in This RU: Oracle has collaborated with state‑of‑the‑art models from Anthropic and OpenAI to proactively identify and fix potential security vulnerabilities. The upcoming […]

HiddenMerit Daily · Issue 58

📊 HiddenMerit Daily · Issue 58 Focus on Database Frontiers, Practical Insights for DBAs July 20, 2026 | 5 Selected Global Breaking News 01|CAICT: Domestic Databases Enter Core System “Deep Water,” AI‑Native Leads New Industry Landscape On July 9, at the 2026 Trustworthy Database Development Conference, CAICT released the “Database Development Research Report (2026).” The report notes that domestic databases have basically completed peripheral system replacement and have officially entered the critical business system breakthrough phase. Key Data: The global database market reached $131.6 billion in 2025 (approximately RMB 894.09 billion). The Chinese database market reached $9.49 billion in 2025 (approximately RMB 64.48 billion), and is expected to reach RMB 97.974 billion by 2028, with a CAGR of 13.06%. The number of domestic database vendors has shrunk from a peak of 167 to 94, with a clear head‑concentration effect and an intensifying “Matthew effect.” AI‑Native Becomes the Main Theme: The report points out that database technology is accelerating its evolution toward the AI‑native direction, and the global database industry is entering a new phase of landscape restructuring. The role of databases is upgrading from “underlying support systems” to “core engines enabling intelligent decision‑making and business innovation.” AI agents are becoming […]

HiddenMerit Daily · Issue 56

📊 HiddenMerit Daily · Issue 56 Focus on Database Frontiers, Practical Insights for DBAs July 6, 2026 | 5 Selected Global Breaking News 01|Kingware Releases Manufacturing Scenario Database Evolution White Paper: SQL Server Replacement Enters “Deep Water” On July 5, CETC Kingware published a technical article titled “Database Evolution in Manufacturing Scenarios: How Kingware Replaces SQL Server,” pointing out that the data surge on industrial shop floors has exceeded the processing capacity of single‑node systems. Traditional database architectures that rely on vertical scaling are facing unprecedented performance challenges. Over the next 1‑3 years, manufacturing enterprises will no longer face only the “choice of replacement,” but must answer the strategic question: “How do we build an autonomous data foundation in the context of de‑IOE?” Three Paradigm Shifts: Hybrid Workloads Become the Norm: The IT architecture of modern factories is shifting from separated OLTP and OLAP to HTAP mode. The same data system must simultaneously handle tens of thousands of device instruction writes per second and minute‑level production report analysis. Distributed Architecture Becomes a Hard Requirement: When a single table exceeds 100 million rows with daily increments exceeding 1 million rows, the index maintenance cost of traditional single‑node databases rises exponentially. Autonomous […]
生成中...
扫描二维码
扫描二维码