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

为何PostgreSQL中使用ctid实现乐观锁会失败?

为什么PostgreSQL的ctid不能用于实现乐观锁?

核心原因是ctid是行的物理存储标识,而非逻辑版本号,它的更新逻辑和乐观锁的需求完全不匹配:

1. ctid的本质与更新行为

ctid表示行在磁盘上的物理位置(格式为(块号, 行号)),只有当行被物理移动时(比如触发TOAST存储、VACUUM操作导致的行迁移、表膨胀引发的重排),ctid才会改变。而绝大多数常规UPDATE操作(比如仅更新普通字段)不会移动行的物理位置,此时ctid保持不变。

在你的测试场景中:

  • 窗口A执行UPDATE后,行的物理位置没有变化,ctid依然是(0,1)
  • 窗口B的UPDATE条件包含ctid=(0,1),当窗口A提交事务后,窗口B的事务继续执行时,当前行的ctid仍与条件匹配,因此UPDATE执行成功,导致rest_count被重复修改为-1,违背了乐观锁的预期。

2. 自定义version列的工作逻辑

自定义version列是逻辑版本标识,它的行为完全可控:每次执行UPDATE时,会主动将version值递增(比如SET version = version + 1),同时将原始version值作为UPDATE条件的一部分。

在测试2中:

  • 窗口A执行UPDATE后,version从1变为2并提交
  • 窗口B的UPDATE条件是version=1,此时行的实际version已经是2,条件不匹配,因此返回UPDATE 0,不会修改数据,完美实现乐观锁的冲突检测。

总结

  • ctid仅适用于定位行的物理位置(比如底层维护、数据修复场景),不能作为乐观锁的版本标识,因为它的变化依赖行的物理移动,而非逻辑更新。
  • 实现乐观锁的正确方式是使用自定义的version列(或timestamp列,注意timestamp的精度问题),通过主动递增版本号并在UPDATE条件中校验版本值来实现冲突检测。

内容的提问来源于stack exchange,提问作者Michelle Liao

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 00:52:35