PostgreSQL 9.5:SELECT … SKIP LOCKED(含 FOR UPDATE/SHARE)源码级深度解析与9.5→18演进
·
PostgreSQL 9.5:SELECT … SKIP LOCKED(含 FOR UPDATE/SHARE)源码级深度解析与9.5→18演进
本文系统解析 PostgreSQL 9.5 引入的 SKIP LOCKED 能力(常与行级锁 SELECT … FOR UPDATE/SHARE 联用),从语法、行为到源码路径与执行流程,帮助读者建立“知其然更知其所以然”的系统性认知;并概览后续版本的演进。包含:概述、背景、名词解释、语法与行为、源码映射、并发与锁管理、性能与配置、兼容性与迁移、Mermaid 流程图(配色优化)、参考资料与“速记口”总结。
简介与项目背景
在任务队列/并行消费者等场景中,经典做法是“选出一批待处理行并加锁”,但在高并发下,行级锁冲突会导致阻塞、队头等待放大。SKIP LOCKED 允许在选取并加锁行时跳过已被其他事务锁住的行,避免阻塞并提升吞吐与整体时延稳定性。
典型应用:多进程/多线程从同一表拉取“未处理任务”,使用 SELECT … FOR UPDATE SKIP LOCKED 块并发消费,已被其它消费者锁住的行会被跳过,避免互相等待。
名词解释
- Row-level lock(行级锁):通过
SELECT … FOR UPDATE/SHARE等语法对元组加锁,保证并发修改/读取的正确性。 - SKIP LOCKED:当目标行被其它事务持有行级锁时,选择跳过该行,而不是等待锁释放。
- NOWAIT:遇到锁冲突立即报错(不同于 SKIP LOCKED 的“跳过”)。
- Blocking/Non-blocking:阻塞式等待锁 vs 非阻塞式跳过或报错。
语法与行为
- 语法要点(9.5 起支持,常用搭配如下):
SELECT … FROM tbl WHERE … FOR UPDATE SKIP LOCKEDSELECT … FROM tbl WHERE … FOR SHARE SKIP LOCKED
- 行为语义:
- FOR UPDATE/SHARE:对目标行加行级锁(UPDATE/SHARE 强度不同)。
- SKIP LOCKED:如果该行当前被其他事务持锁,则本次选择中直接跳过该行(不阻塞、不报错)。
- 与 NOWAIT 的差异:
- NOWAIT:遇锁即报错,适用于不希望等待也不希望跳过的场景。
- SKIP LOCKED:遇锁即跳过,适用于“尽快获取可处理行”的队列/并发消费场景。
示例 SQL(更多示例):sql/select_skip_locked_examples.sql
源码映射与实现要点
SKIP LOCKED 的实现涉及解析器、锁管理与执行器三大层面:
- 解析与语法(LockingClause 扩展):
- 语法定义与解析:
src/backend/parser/gram.y - 解析产物(如 LockingClause)在计划与执行阶段控制加锁策略与行为(包括 SKIP/NOWAIT)。
- 语法定义与解析:
- 锁管理与存储层:
- 锁管理(LMGR):
src/backend/storage/lmgr/ - 行级锁/元组锁路径与冲突判断:
src/backend/access/heap/
- 锁管理(LMGR):
- 执行器:
- 执行加锁与跳过策略的实际逻辑:
src/backend/executor/
- 执行加锁与跳过策略的实际逻辑:
说明:本文以目录/文件路径级别给出源码位置,函数级调用链可在上述路径内进一步检索,如元组锁获取、冲突检查与“跳过”分支处理。
并发与锁管理关系
- 目标:在“获取一批可处理行”的过程中最大化非阻塞性。
- 行级锁获取策略:
- FOR UPDATE/SHARE 在扫描器尝试锁定元组时,若发现元组已被持锁:
- NOWAIT:抛错返回。
- SKIP LOCKED:跳过该元组,继续扫描下一候选行。
- FOR UPDATE/SHARE 在扫描器尝试锁定元组时,若发现元组已被持锁:
- 可见性与快照:
- 在 MVCC 下,扫描基于快照判断可见行;锁获取是附加操作。对已锁住但可见的行,SKIP LOCKED 将“可见但不可立即锁定”的行跳过,避免等待。
- 饥饿与公平性:
- SKIP LOCKED 提升吞吐,但可能导致部分热门行在高并发竞争下“长时间被跳过”。实际业务需配合调度策略(批次、优先级、重试机制)缓解。
性能与配置建议
- 索引选择与过滤:
- 队列获取场景通常结合状态列与索引(如 status、priority、created_at),降低扫描成本。
- 批量与分片:
- 可分批次进行
LIMIT并配合分片/分区,减少同一热点范围的锁竞争。
- 可分批次进行
- 监控:
- 关注长尾行被跳过次数与处理延迟,必要时做“无锁阶段重扫”或迁移到其他分片。
- 与 NOWAIT 的取舍:
- NOWAIT 更适合“希望在应用层处理冲突”的场景;SKIP LOCKED 更适合“希望尽快拿到可处理集合”的场景。
兼容性与迁移
- 从阻塞式 FOR UPDATE 迁移:
- 在不希望等待锁的场景,将原有
FOR UPDATE替换为FOR UPDATE SKIP LOCKED;评估业务语义是否允许跳过(例如任务必须按严格顺序处理的场景不适合)。
- 在不希望等待锁的场景,将原有
- 错误处理与重试:
- 与 NOWAIT 相比,SKIP LOCKED 不产生错误;应用层需考虑“跳过行”的再处理策略(重试、回收机制)。
Mermaid:并发取数与加锁流程(配色与样式优化)
为缓解视觉疲劳与提升可读性,统一采用柔和配色与节点样式:
从 9.5 到 18 的相关演进概览(与 SKIP LOCKED 关联的方向)
- 9.5:引入 SKIP LOCKED(与 FOR UPDATE/SHARE 联用),在并发消费与队列场景显著提升吞吐与稳定性。
- 9.6:并行查询与执行器优化增强;在大数据量扫描下受益(配合索引与过滤)。
- 10:声明式分区引入;在分区环境中将热点任务分布到多个分区可进一步降低竞争。
- 11~12:优化器与索引层改进,间接改善扫描与加锁的整体成本。
- 13~14:B-Tree 与 I/O 优化(如页面管理/去重),降低写放大与冲突热点。
- 15~18:稳定性与可维护性增强;SKIP LOCKED 作为成熟能力保持稳定,配合更丰富的调度/分区策略。
注:上述为与 SKIP LOCKED 关联的宏观方向;具体版本细节以各版本 Release Notes 与文档为准。
参考资料与权威链接
- PostgreSQL 9.5 Release Notes(官方):https://www.postgresql.org/docs/9.5/release-9-5.html
- 行级锁与并发控制文档(官方):https://www.postgresql.org/docs/current/explicit-locking.html
- 解析与锁管理相关目录:
- 语法解析:
src/backend/parser/gram.y - 锁管理:
src/backend/storage/lmgr/ - Heap/元组访问:
src/backend/access/heap/ - 执行器:
src/backend/executor/
- 语法解析:
速记口(面试/设计复盘快速记忆)
- 关键词:SKIP LOCKED、FOR UPDATE/SHARE、行级锁、非阻塞、队列并发。
- 语法骨架:
SELECT … FOR UPDATE|SHARE SKIP LOCKED;NOWAIT 为“遇锁报错”。 - 源码定位:解析
src/backend/parser/gram.y→ 锁管理src/backend/storage/lmgr/→ Heap访问src/backend/access/heap/→ 执行器src/backend/executor/。 - 并发语义:遇锁跳过/不等待,提升吞吐但可能产生“热门行长时间被跳过”,需配合调度策略。
- 性能要点:索引与过滤、批量/分片、监控跳过次数与处理延迟。
- 迁移提示:从阻塞式 FOR UPDATE 迁移时确认业务允跳过;必要时与 NOWAIT/重试策略结合。
附注:本文通过路径级引用标注源码位置以保持跨版本稳定性与可溯源性;如需函数级细节,可在上述目录中检索具体实现(行锁获取、冲突判断、跳过分支与执行器遍历逻辑)。
更多推荐



所有评论(0)