伪代码:
-
开始存储过程
-
提交
-
检查a行//步骤1 第1列的值
-
更新a行第1列//步骤2
-
提交
-
结束存储过程
这个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包构建自己的锁定过程。但是,默认情况下,不会将此程序包的权限授予任何人;这是因为构建自定义锁定例程很棘手。