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
相关产品推荐
相关产品推荐

