Oracle乐观锁最稳实现是UPDATE语句中SET version = version + 1且WHERE version = :expected_version;需显式传入old_version、检查SQL%ROWCOUNT、返回冲突信号,禁用ORA_ROWSCN替代version。
Oracle存储过程中用version字段做乐观锁更新
直接在update语句里加where version = :old_version条件,是最轻量、最可控的实现方式。oracle不提供原生的“乐观锁语法糖”,全靠sql条件和返回行数判断是否更新成功。
- 表必须有
version(或ver_no等)数值型字段,初始值为1,每次成功更新后+1 - 存储过程参数需显式传入读取时的
old_version值,不能依赖SELECT ... FOR UPDATE——那属于悲观锁行为 - 执行
UPDATE后立即检查SQL%ROWCOUNT:等于0说明版本已变,冲突发生 - 不要在
UPDATE里写version = version + 1再单独SELECT查新值——这会引入竞态窗口
UPDATE ... WHERE version = ?语句必须带version = version + 1
版本号递增必须和数据更新在同一原子语句中完成,否则可能造成version被跳过或重复使用。Oracle不支持RETURNING直接返回新version,所以推荐写法是:
UPDATE orders
SET status = :new_status,
last_modified = SYSDATE,
version = version + 1
WHERE id = :id AND version = :expected_version;
注意:version = version + 1是写在SET子句里的,不是单独语句;:expected_version来自应用层或调用方传入的原始读值。
存储过程里怎么处理乐观锁失败
不能只抛异常就完事。Oracle存储过程需要明确返回冲突信号,让调用方决定重试还是提示用户。常见做法是用OUT参数或自定义异常码:
- 声明
out_result OUT NUMBER:成功=1,冲突=0,其他错误=-1 - 更新后
IF SQL%ROWCOUNT = 0 THEN out_result := 0; RETURN; END IF; - 避免用
RAISE_APPLICATION_ERROR中断整个事务——除非业务要求强一致性回滚 - 如果调用方是Java或Python,建议统一返回
0并附带ORA-20001: version mismatch,比捕获通用异常更易识别
别把ORA_ROWSCN当version直接用
ORA_ROWSCN是伪列,精度依赖COMPATIBLE参数和块级SCN机制,不是行级严格单调递增的版本号。它可能:
- 多个更新落在同一块内时,
ORA_ROWSCN值相同,导致误判“无冲突” - 启用
ROWDEPENDENCIES后才支持行级SCN,但建表时就得指定,已有表无法在线修改 - 无法在
WHERE子句中可靠参与比较(尤其涉及索引扫描时,优化器可能忽略) - 真正要用时间维度做乐观锁,不如用
last_modified+timestamp组合,且确保ON UPDATE CURRENT_TIMESTAMP行为可控
真要省字段又想准,就老老实实加version NUMBER DEFAULT 1,配合UPDATE ... SET version = version + 1 WHERE ... AND version = ?——这是Oracle下最稳的乐观锁落地点。











