You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL可重复读级别下SKIP LOCKED触发序列化错误排查

PostgreSQL可重复读隔离级别下SKIP LOCKED仍报序列化访问错误的原因

场景复现

  • 事务1(可重复读隔离级别)执行带SKIP LOCKED的行锁查询:
select * from booker.review_tasks where review_task_id in (
    2140285001,
    2140285031,
    2140304551
) for update skip locked;
  • 并发事务2对同表ID匹配的行执行更新:
update booker.review_tasks set priority = 190 where review_task_id in (
    2140285001,
    2140304551
);
  • 事务1抛出错误:
was aborted: ERROR: could not serialize access due to concurrent update  Call getNextException to see other errors in the batch.

    at com.amazon.contentreviewplatformservice.db.RWNamedJdbcTemplate.batchUpdate(RWNamedJdbcTemplate.java:126) ~[ContentReviewPlatformService-1.0.jar:?]
    at com.amazon.contentreviewplatformservice.db.dao.DAOUtils.changeUserTaskAssignmentStatus(DAOUtils.java:126) ~[ContentReviewPlatformService-1.0.jar:?]
    at com.amazon.contentreviewplatformservice.workflow.dao.WorkflowManagerDAO.changeReviewState(WorkflowManagerDAO.java:784) ~[ContentReviewPlatformService-1.0.jar:?]

核心原因

对SKIP LOCKED的作用边界、PostgreSQL可重复读(RR)隔离级别的规则存在两个常见认知偏差:

  1. SKIP LOCKED的作用范围有严格限制
    SKIP LOCKED只会跳过「执行加锁动作的瞬间,被其他未提交事务持锁的行」,它的执行逻辑顺序是:
  • 第一步:基于当前事务的一致性快照,扫描匹配WHERE条件的行版本
  • 第二步:逐行尝试对最新版本的行加FOR UPDATE锁
  • 第三步:如果加锁时发现行已被其他未提交事务锁定,直接跳过该行;如果加锁成功就返回该行
    它不会处理「行在事务快照生成后,已经被其他事务修改并提交」的场景,这类场景的处理优先级远高于SKIP LOCKED逻辑。
  1. 可重复读隔离级别下的行版本冲突会直接触发序列化错误
    可重复读隔离级别的核心要求是:事务整个生命周期内看到的数据,和事务启动时创建的一致性快照完全一致。当事务1扫描到目标行、尝试加锁时,如果发现该行已经被其他并发事务修改并提交(即行的最新版本和快照里的版本不一致),PostgreSQL不会绕过一致性要求去给旧版本行加锁,也不会自动读取最新版本的行,会直接抛出could not serialize access due to concurrent update错误,这个流程根本不会走到SKIP LOCKED的判断逻辑。

关于「已被锁定的行为何能被其他事务并发更新」的说明

假设的「事务1先锁行、事务2再更新」的执行时序完全不成立:事务1根本没有先拿到那两行的锁。真实的并发时序大概率是:

  • 事务1启动,生成可重复读级别的一致性快照,但还没扫描到目标ID的行,自然没有加任何锁
  • 事务2启动,率先拿到2140285001、2140304551两行的行锁,执行更新后直接提交,释放锁
  • 事务1此时才扫描到这两行,尝试加锁时检测到行版本和自己的快照不一致,直接抛出序列化错误
    在事务1成功给行加上锁之前,其他事务完全可以正常对行执行更新、加锁操作,SKIP LOCKED没有任何提前占锁的能力。

规避方案

如果需要SKIP LOCKED正常跳过被锁行、不抛出序列化错误,有两种可选方案:

  • 将事务隔离级别调整为读提交(RC):该级别下碰到行版本更新时,会自动读取最新提交的行版本,重新匹配WHERE条件后再加锁,碰到仍被未提交事务锁定的行才会触发SKIP LOCKED逻辑跳过
  • 调整业务逻辑,保证SELECT ... FOR UPDATE SKIP LOCKED语句在事务启动后第一时间执行,减少和其他更新事务的冲突窗口

内容的提问来源于stack exchange,提问作者Jeffin Sam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 21:01:15