导读:本期聚焦于北京GEO公司创作的《PostgreSQL中的锁升级与锁转换是什么?底层机制与实战解析》,敬请观看详情。数据库并发控制一直是PostgreSQL学习中容易踩坑的部分,其中锁升级和锁转换这两个概念经常被混为一谈。本文从PostgreSQL的多粒度锁体系入手,先厘清表级锁、行级锁和页级锁各自的作用范围,再解释锁转换在行锁更新场景中的实际含义,说明PostgreSQL与某些数据库不同,本身并不支持自动的锁升级机制,并分析这种设计对高并发写入性能的影响。文中结合mvcc机制、行锁的四种模式、死锁检测日志等内容给出排查思路,同时提供避免长事务、减少锁冲突的实践建议,适合后端开发者和数据库运维人员阅读参考。

在PostgreSQL的并发控制体系中,锁是一个绕不开的话题。不少人在阅读文档时会遇到两个相近的概念:锁升级和锁转换。前者指数据库把大量细粒度锁合并为粗粒度锁以节省资源,后者指同一对象上的锁模式发生变更。这两个词看起来只差一个字,但PostgreSQL对它们的处理方式差异很大,甚至可以说PostgreSQL根本就没有传统意义上的锁升级。理解这一点,对排查锁等待、死锁以及高并发写入下的性能问题非常有帮助。

PostgreSQL中的锁升级与锁转换是什么?底层机制与实战解析

一、PostgreSQL的多粒度锁体系

PostgreSQL内部维护着一套层次化的锁结构,最上层是数据库级,往下依次是表级、页级,最底层是行级(元组级)。不同粒度的锁各司其职:表级锁控制对整个表的结构性访问,比如ALTER TABLE需要拿到排他的AccessExclusiveLock;行级锁则只作用于单条元组,用于保证并发更新同一行时的正确性。

与一些数据库使用集中的锁表管理所有锁不同,PostgreSQL的常规行级锁并不占用共享内存中的锁槽位,而是直接存储在元组头部(通过t_ctid指向行锁链表)。这意味着理论上一个事务可以持有数百万个行锁而不会耗尽锁资源。正是因为这个设计,PostgreSQL从架构上就不需要锁升级——没有锁资源的上限压力,自然没有把行锁合并成表锁的动机。

不过也有例外情况。在某些操作中PostgreSQL会显式地一次性行锁升级为表级锁,例如执行ALTER TABLE、CLUSTER、VACUUM FULL时,系统会直接在表级别加更强的锁,这更准确地说是锁请求粒度的切换,而不是运行时渐进式的升级过程。

二、锁转换:行锁模式之间的变更

锁转换指的是对同一个锁定对象,事务在不同阶段请求更强的锁模式。在行级层面,PostgreSQL提供了四种锁模式:FOR KEY SHARE、FOR SHARE、FOR NO KEY UPDATE、FOR UPDATE。它们之间存在兼容矩阵,例如FOR UPDATE与FOR KEY SHARE冲突,但FOR NO KEY UPDATE与FOR KEY SHARE可以共存。

一个典型的锁转换场景出现在外键检查中:子表插入时会对父表对应行加FOR KEY SHARE锁;如果随后父表那一行被UPDATE修改了键值,就需要把锁转换为FOR NO KEY UPDATE或更强的模式。再比如SELECT FOR SHARE之后又对该行执行UPDATE,行锁会转换为FOR UPDATE。这种转换是PostgreSQL内部自动完成的,转换过程中如果与其他事务持有的锁冲突,就会产生等待。

可以通过下面的SQL观察行锁的持有与等待情况:

-- 会话1:锁定一行
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;

-- 会话2:查看锁等待关系
SELECT pid, locktype, mode, granted
FROM pg_locks
WHERE locktype = 'transactionid' OR locktype = 'tuple';

-- 会话3:尝试更新同一行,会被阻塞
UPDATE accounts SET balance = balance - 100 WHERE id = 1;

在pg_locks视图中,locktype为tuple的记录就是行级锁,granted为false表示正在等待。结合pg_stat_activity可以进一步定位是哪个会话造成了阻塞,这是排查锁冲突最直接的手段。

三、PostgreSQL为什么不做自动锁升级,以及实践建议

在SQL Server等数据库中,当事务持有的行锁数量超过阈值时,锁管理器会自动升级为表锁,以减少内存开销,代价是并发度急剧下降。PostgreSQL的行锁存储在数据行本身而非集中内存结构中,持有成本极低,因此官方从未实现自动锁升级。这带来的好处是并发粒度始终保持在行级,坏处是某些本可以快速完成的批量操作会持有海量行锁,元组头部的锁链表变长后,锁冲突检测的开销也会增加。

对于需要修改全表大部分数据的场景,一个务实的做法是绕过行锁机制,比如使用无谓词的UPDATE触发表级ShareLock思路,或者直接分段处理。此外,几个实践建议值得遵守:第一,避免长事务,事务持有锁的时间越长,冲突概率越高;第二,UPDATE和DELETE的WHERE条件务必带上索引列,否则扫描过程中产生的行锁范围会远大于预期;第三,多表更新保持一致的加锁顺序,从根源上减少死锁。

死锁一旦发生,PostgreSQL的检测器会终止其中一个事务,并在日志中输出详细的过程信息,包括两个事务各自执行的语句和等待的锁模式。定期关注这类日志,比事后猜测更能帮助发现业务代码中隐藏的加锁顺序问题。总的来说,理解PostgreSQL没有锁升级、只有显式锁粒度切换和行锁模式转换这一事实,能让你在面对锁等待问题时少走很多弯路。

PostgreSQL锁行级锁表级锁修改时间:2026-09-11 18:16:30

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0911/54824.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。