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

Oracle中两个会话更新同一值是否会死锁?自定义锁防函数并行可行吗?

方案评估结论

你当前的方案无法稳定实现单实例运行的需求,虽不会产生死锁,但存在更严重的永久锁死风险。


现有方案的核心问题

  • 你在更新完flag标记获取锁之后立刻执行了commit,这个操作虽然保证了更新动作的原子性,但一旦业务逻辑执行过程中会话异常中断(比如连接断开、程序崩溃、服务器宕机),flag会永久停留在'In Use'状态,后续所有请求都无法拿到锁,必须人工介入修改表数据才能恢复功能。
  • 仅靠业务逻辑维护标记位没有和数据库事务/锁绑定,不属于原生原子性的锁机制,极端场景下如果出现标记位误改会直接导致锁逻辑失效。
  • 死锁风险方面:你的方案本身不会触发死锁,整个流程只涉及单表单行的更新操作,不存在多资源交叉竞争的场景,没有死锁产生的条件。

推荐的可靠实现方案

建议直接使用Oracle原生锁机制,不需要人工维护标记位,完全避免永久锁死问题:

简化版DDL(不需要flag字段)

create table test_oracle_lock (id int primary key);
-- 预先插入一条固定id的记录即可
insert into test_oracle_lock values(1);
commit;

函数内锁逻辑

DECLARE
  v_lock_id INT;
BEGIN
  -- 尝试获取行锁,不等待,拿不到直接抛出资源忙异常
  SELECT id INTO v_lock_id FROM test_oracle_lock WHERE id = 1 FOR UPDATE NOWAIT;

  -- 此处执行业务逻辑SQL
  -- ...

  -- 执行完成后提交事务,自动释放锁
  COMMIT;
EXCEPTION
  WHEN OTHERS THEN
    -- 不管是拿锁失败还是业务报错,都回滚释放锁,直接退出
    ROLLBACK;
    EXIT;
END;

方案优势

  • 完全由Oracle原生锁机制保证同一时间只有一个会话能拿到锁,不会出现并行执行的情况
  • 会话异常中断时,数据库会自动回滚未提交的事务,直接释放锁,不需要人工维护任何标记位,不存在永久锁死的风险
  • 逻辑更简单,不需要额外维护标记位的更新操作,出问题的概率更低

特殊场景兼容方案

如果业务逻辑中存在必须中间提交的场景,无法用事务绑定行锁,可以使用Oracle内置的DBMS_LOCK包实现用户级锁,锁的生命周期和会话绑定,也不会出现永久锁死的问题:

DECLARE
  v_lock_handle VARCHAR2(128);
  v_lock_result INT;
BEGIN
  -- 生成唯一锁标识
  DBMS_LOCK.ALLOCATE_UNIQUE(lockname => 'MY_FUNC_LOCK', lockhandle => v_lock_handle);
  -- 尝试获取锁,等待时间为0,拿不到立刻返回
  v_lock_result := DBMS_LOCK.REQUEST(lockhandle => v_lock_handle, lockmode => DBMS_LOCK.X_MODE, timeout => 0, release_on_commit => FALSE);
  
  IF v_lock_result != 0 THEN
    -- 拿锁失败,直接退出
    EXIT;
  END IF;

  -- 此处执行业务逻辑,哪怕中间提交事务也不会释放锁
  -- ...

  -- 执行完成后手动释放锁
  DBMS_LOCK.RELEASE(v_lock_handle);
END;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 10:09:03