Oracle存储过程线程安全



伪代码:

  1. 开始存储过程

  2. 提交

  3. 检查a行//步骤1 第1列的值

  4. 更新a行第1列//步骤2

  5. 提交

  6. 结束存储过程

这个sp线程安全吗?

编辑:

Declare
  tag_rec prep_tag%ROWTYPE;
begin
  COMMIT; 
  SELECT * INTO tag_rec 
  FROM PREP_TAG 
  WHERE project = 'a' and categoryId = 'b'; 
  if tag_rec.locked = 'No' then
    UPDATE prep_tag 
    SET locked = 'Yes' 
    WHERE TAG_NUMBER = tag_rec.TAG_NUMBER;
  end if;
  COMMIT; 
end;

这个sp线程安全吗?线程A是否检查了tag_rec.locked="否",然后就要更新它了。但在更新之前,线程B偷偷溜了进来,也看到了tag_rec.locked=‘否’?

问题的简短答案是否定的,这不是线程安全的。其他会话可能会进入并在SELECT和update语句之间更新prep_tag。

长期的答案是你这样做的方式不对。听起来你想更新那些记录的锁定字段。你也可以这么做:

UPDATE prep_tag SET locked = 'Yes' 
 WHERE project = 'a' 
   AND categoryId = 'b'
   AND locked = 'No'

一句话,它就保证是原子的。

此外,我建议不要在您的区块内提交,假设这个交易有更多内容。

Oracle不会锁定SELECT(除非有FOR UPDATE子句),因此行有可能在SELECT和UPDATE之间更新。

但更可能的情况是,行在选择之前已经更新,而没有提交。SELECT将显示当前提交的状态。然后UPDATE就会出现并等待锁。

假设您不想阻止会话,请查看SELECT。。。FOR UPDATE NOWAIT,并处理异常。另一种选择是可串行隔离级别。如果试图更改事务开始时不是最新的数据,那么基本上会抛出"ORA-08177无法序列化对此事务的访问"错误。

您不需要推出自己的检查机制。Oracle已经通过其SELECT。。。FOR UPDATE语法。WAIT|NOWAIT控制所选行被锁定时的行为(立即等待或失败)。了解更多信息。(在11g中,Oracle暴露了SKIP ROWS条款,该条款允许我们实现自己的排队机制。)

如果您需要更复杂的锁定过程,Oracle允许我们使用DBMS_LOCK包构建自己的锁定过程。但是,默认情况下,不会将此程序包的权限授予任何人;这是因为构建自定义锁定例程很棘手。

最新更新