如何在Oracle中实现多存储过程并发执行时的悲观锁控制

轻静大大_9501

轻静大大_9501

2026-08-31

575人浏览

原创

存储过程本身不带锁,因其仅为pl/sql代码块,在各自会话事务中运行,不自动锁定数据;若无显式select...for update等锁机制,多会话并发更新同一行将导致覆盖写。

如何在oracle中实现多存储过程并发执行时的悲观锁控制

Oracle中多存储过程并发执行时,不能靠存储过程自身“加锁”,必须显式使用 SELECT ... FOR UPDATE 或事务级行锁机制,否则根本不存在真正意义上的悲观锁控制。

为什么存储过程本身不带锁

存储过程只是封装好的 PL/SQL 代码块,执行时默认在各自会话的事务上下文中运行。它不会自动锁定任何数据——哪怕两个过程都更新同一张表的同一行,若没加锁语句,就会发生覆盖写(lost update)。常见错误是以为“调用同一个存储过程”就天然串行化,实际完全不是。

  • CREATE OR REPLACE PROCEDURE 定义本身不携带锁语义
  • 过程内若只写 UPDATE ... WHERE id = ?,没有前置 SELECT ... FOR UPDATE,就是纯乐观更新逻辑
  • 多个会话并发执行该过程,可能同时读到旧值、同时计算、同时写入,后提交者覆盖前提交者

正确做法:在过程内嵌入 SELECT ... FOR UPDATE

悲观锁的核心动作是“先查后锁”,必须在修改前锁定目标行,并确保整个操作在同一个事务中完成。典型结构如下:

CREATE OR REPLACE PROCEDURE update_balance(p_acct_id NUMBER, p_amount NUMBER) AS
  v_curr_bal NUMBER;
BEGIN
  -- 关键:显式加锁,且必须指定 WAIT 或 NOWAIT 行为
  SELECT balance INTO v_curr_bal
    FROM accounts
   WHERE acct_id = p_acct_id
     FOR UPDATE WAIT 5;  -- 等待最多 5 秒,超时抛 ORA-30006
<p>IF v_curr_bal + p_amount </p><div class="aritcle_card flexRow artxards">
											<div class="artcardd flexRow">
												<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill6971" title="QuantOracle"><img
														src="https://img.php.cn/upload/skill/000/000/081/179120536643782.jpg" alt="QuantOracle" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
												<div class="aritcle_card_info flexColumn">
													<a rel="nofollow" href="/xiazai/skill6971" title="QuantOracle" class="overflowclass">QuantOracle</a>
													<p class="overflowclass">63个确定性量化金融计算器 + 10个通过MCP的复合工作流。期权定价、Greeks、奇异衍生品、风险指标、投资组合优化……</p>
												</div>
												<a rel="nofollow" href="/xiazai/skill6971" title="QuantOracle" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
												</a>
											</div>
										</div><p>UPDATE accounts SET balance = balance + p_amount
WHERE acct_id = p_acct_id;</p><p>COMMIT;  -- 显式提交释放锁;若用自治事务则另当别论
EXCEPTION
WHEN TIMEOUT_ON_RESOURCE THEN
RAISE_APPLICATION_ERROR(-20002, 'Account locked by another session');
END;</p>
  • FOR UPDATE WAIT n 是生产环境首选,避免无限阻塞
  • 不要省略 INTO 变量——SELECT ... FOR UPDATE 必须有结果集绑定,否则语法报错
  • 锁范围由 WHERE 条件决定:条件越精确(如主键),锁粒度越小,竞争越低
  • 如果过程被频繁调用,WAIT 0(即 NOWAIT)可快速失败,由应用重试或降级处理

容易踩的坑:事务边界与锁生命周期

锁只在事务结束(COMMIT 或 ROLLBACK)时释放。很多开发者忽略这点,导致锁意外长期持有:

  • 忘记 COMMIT 或 ROLLBACK —— 锁持续到会话断开,可能卡住其他业务
  • 在包(package)中使用全局变量缓存连接状态,误以为“过程结束=事务结束”,其实事务仍活跃
  • 调用链中混用自治事务(AUTONOMOUS_TRANSACTION):自治事务内的 FOR UPDATE 不影响外层事务锁,极易造成逻辑错乱
  • 异常未捕获导致事务未回滚:WHEN OTHERS THEN NULL 是高危写法,应至少 ROLLBACK 并重抛

复杂点在于锁等待链和死锁风险

多个存储过程若按不同顺序访问多张表(如过程 A 先锁 orders 再锁 inventory,过程 B 反之),就可能触发 Oracle 自动检测到的死锁(ORA-00060)。这不是代码写错,而是设计缺陷:

  • 所有涉及悲观锁的过程,必须严格约定资源访问顺序(如总是先锁主表再锁明细表)
  • 避免在循环中逐条 SELECT ... FOR UPDATE,尤其当循环条件不可控时,锁数量和时间不可预估
  • 监控锁等待最直接的方式是查 V$SESSION_WAIT 和 V$LOCK,但线上出问题时往往已晚——关键在设计阶段就规避

真正难的不是写对一行 FOR UPDATE,而是让所有相关过程在并发路径上保持一致的锁策略、顺序和事务粒度。一旦跨过程协作,单点加锁解决不了系统级竞争。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4503

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

931

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1189

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

6461

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

3243

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

6420

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

8781

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1190

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1052

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程