Oracle SELECT FOR UPDATE - 演示?

时间:2017-07-01 21:51:33

标签: sql oracle transactions locking

我很难理解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表中的行吗?

我已经阅读了有关会话的内容:来自同一会话的查询并不关心锁定。我可以在单个脚本文件中以某种方式演示锁定功能吗?例如,我可以在一个脚本中运行两个不同的会话,因此第二个调用将检索一个错误,表明该行已被锁定?

2 个答案:

答案 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客​​户端无法更改任何此记录。因此,如果您想测试其工作原理,请创建两个单独的客户端连接,并首先尝试在一个会话中锁定记录,然后尝试在另一个会话中更新相同的记录。