我很难理解SELECT FOR UPDATE的锁定功能。
这是我尝试过的。
CREATE TABLE ACCOUNT_TAB (
ACC_ID int NOT NULL PRIMARY KEY,
BALANCE int NOT NULL
);
INSERT INTO ACCOUNT_TAB
VALUES(1, 100);
SELECT * FROM ACCOUNT_TAB FOR UPDATE;
SELECT * FROM ACCOUNT_TAB;
SELECT都会检索该行,但是第一个查询不应该锁定ACCOUNT_TAB表中的行吗?
我已经阅读了有关会话的内容:来自同一会话的查询并不关心锁定。我可以在单个脚本文件中以某种方式演示锁定功能吗?例如,我可以在一个脚本中运行两个不同的会话,因此第二个调用将检索一个错误,表明该行已被锁定?
答案 0 :(得分:3)
您的原始实验未能证明锁定,因为在Oracle写入时不会阻止读取。 FOR UPDATE子句允许我们避免两个会话尝试写到同一记录的情况;任意数量的会话都可以读取记录。
“好的,但是,有没有办法在单个脚本文件中演示锁?”
是。这是一个带有本地过程的脚本,它使用autonomous_transaction
pragma来模拟多用户环境:
declare
procedure p1 (p_id in number) is
pragma autonomous_transaction;
cursor c23 is
select * from t23
where id = p_id
for update nowait;
r23 c23%rowtype;
begin
dbms_output.put_line('nested transaction');
open c23;
fetch c23 into r23;
update t23
set col2 = col2 * 2;
close c23;
commit;
exception
when others then
dbms_output.put_line(sqlerrm);
end;
begin
update t23
set col1 = 2
where id = 1;
p1 (1);
commit;
end;
/
第一个UPDATE语句发出一个锁,导致程序调用失败,因为它无法获得锁定(由于使用了NOWAIT子句):
...
30 end;
31 /
nested transaction
ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
PL/SQL procedure successfully completed.
SQL>
答案 1 :(得分:2)
RDBMS将在SELECT FOR UPDATE语句标识的所有行上获得独占的行级锁,因此只有您是唯一允许更新它们的行。这意味着在您执行COMMIT或ROLLBACK之前,其他RDBMS客户端无法更改任何此记录。因此,如果您想测试其工作原理,请创建两个单独的客户端连接,并首先尝试在一个会话中锁定记录,然后尝试在另一个会话中更新相同的记录。