Oracle存储过程是否是线程安全的?

时间:2011-05-31 23:15:17

标签: oracle concurrency thread-safety

伪代码:

  1. 开始存储过程

  2. 提交

  3. 检查第a行//第1步

  4. 的值第1列
  5. 更新第a行第1行

  6. 提交

  7. 结束存储过程

  8. 这个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 ='不'?

3 个答案:

答案 0 :(得分:7)

对你的问题的简短回答是否定的,这不是线程安全的。其他一些会话可以进入并更新SELECT和UPDATE语句之间的prep_tag。

答案很长,你做错了。听起来您想更新这些记录的锁定字段。你也可以这样做:

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

一个声明,它保证是原子的。

此外,我建议不要在你的区块内部进行交易,假设此交易还有更多内容。

答案 1 :(得分:4)

您不需要推出自己的检查机制。 Oracle已经通过其SELECT ... FOR UPDATE语法提供了此功能。如果所选行被锁定(等待或立即失败),则WAIT | NOWAIT控制行为。 Find out more。 (在11g Oracle exposed the SKIP ROWS clause中允许我们实现自己的排队机制。)

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

答案 2 :(得分:4)

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

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

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