ORACLE并发性锁(for update)测试

FOR UPDATE释放条件
1.数据库连接中断
2.程序停止
3.COMMIT
4.ROLLBACK

 

1.select * from ls.lims_employees where empno='0001' for update
只有当前用户能查看SELECT记录,其他用户可以select * from ls.lims_employees where empno='0001'  查询,但要加for update则必须等待前一个用户提交才能查

 

 

2.SELECT FOR UPDATE SKIP LOCKED选项是ORACLE的一个未公开的特性,它的含义是SELECT时跳过被锁的记录。

考虑下面的例子:

会话1:

SQL> select * from sex_dict ;

 

 SERIAL_NOS SEX_INPUT_CO

---------- - ---- --------

        1 1男   N

        9 0未知 WZ

        2 2女   N

        4 9未定 WD

SQL> select * from sex_dict where serial_no = 1 for update ;

 

 SERIAL_NOS SEX_INPUT_CO

---------- - ---- --------

        1 1男   N

 

会话2:

查询并锁住serial_no in (1,2)的记录

SQL> select * from sex_dict where serial_no in ( 1, 2) for update ;

 

此时会话2挂住,直到会话1事务结束。

 

加上”NOWAIT”选项

SQL> select * from sex_dict where serial_no in ( 1, 2) for update nowait ;

 select * from sex_dict where serial_no in ( 1, 2) for update nowait

              *

ERROR at line 1:

ORA-00054: resource busy and acquire with NOWAIT specified

因为serial_no = 1的记录被会话锁住,所以这个操作没有成功。

 

加上SKIP LOCKED选项

SQL> select * from sex_dict where serial_no in ( 1, 2) for update nowait skip locked;

 

 SERIAL_NOS SEX_INPUT_CO

---------- - ---- --------

        2 2女   N

 

会话2只锁住serial_no=2的记录,跳过serial_no=1的记录。

 

这个特性适合例如消息队列的操作,当消息到达时,处理消息的各个客户选取并锁住一个消息处理,但不能阻塞其它客户处理消息。其它客户则处理没有被锁的消息。这个可以参考AnySQL.net的《在Oracle中如何跳过被别人锁住的记录?》

利用这个特点,Tomac给出了一个不跳号序列号的方法


===========================================

未加for update并发时,会出现超发情况。

declare 
  -- Local variables here
  i integer;
begin
  -- Test statements here
  
   SELECT COL2 INTO I FROM PUSER.AAA WHERE COL1='A1'  ;
   IF I>80 THEN
     UPDATE PUSER.AAA SET COL2=COL2 - 80 WHERE COL1='A1';
   END IF;
end;

declare 
  -- Local variables here
  i integer;
begin
  -- Test statements here
  

加行级锁,等待10秒
   SELECT COL2 INTO I FROM PUSER.AAA WHERE COL1='A1'  for update wait 10;
   IF I>80 THEN
     UPDATE PUSER.AAA SET COL2=COL2 - 80 WHERE COL1='A1';
   END IF;
end;

舉個例子: 假設商品表單 products 內有一個存放商品數量的 quantity ,在訂單成立之前必須先確定 quantity 商品數量是否足夠 (quantity>0) ,然後才把數量更新為 1。

不安全的做法:

SELECT quantity FROM products WHERE id=3;
UPDATE products SET quantity = 1 WHERE id=3;

為什麼不安全呢?
少量的狀況下或許不會有問題,但是大量的資料存取「鐵定」會出問題。

如果我們需要在 quantity>0 的情況下才能扣庫存,假設程式在第一行 SELECT 讀到的 quantity 是 2 ,看起來數字沒有錯,但是當 MySQL 正準備要 UPDATE 的時候,可能已經有人把庫存扣成 0 了,但是程式卻渾然不知,將錯就錯的 UPDATE 下去了。

因此必須透過的交易機制來確保讀取及送交的資料都是正確的。

於是我們在 MySQL 就可以這樣測試: (註1)

SET AUTOCOMMIT=0;
BEGIN WORK;
SELECT quantity FROM products WHERE id=3 FOR UPDATE;
===========================================
此時 products 資料中 id=3 的資料被鎖住(註3),其它交易必須等待此次交易
送交後才能執行 SELECT * FROM products WHERE id=3 FOR UPDATE (註2)
如此可以確保 quantity 在別的交易讀到的數字是正確的。

===========================================
UPDATE products SET quantity = '1' WHERE id=3 ;
COMMIT WORK;
===========================================
送交(Commit)寫入資料庫,products 解鎖。


舉個例子: 假設商品表單 products 內有一個存放商品數量的 quantity ,在訂單成立之前必須先確定 quantity 商品數量是否足夠 (quantity>0) ,然後才把數量更新為 1。

不安全的做法:

SELECT quantity FROM products WHERE id=3;
UPDATE products SET quantity = 1 WHERE id=3;

為什麼不安全呢?
少量的狀況下或許不會有問題,但是大量的資料存取「鐵定」會出問題。

如果我們需要在 quantity>0 的情況下才能扣庫存,假設程式在第一行 SELECT 讀到的 quantity 是 2 ,看起來數字沒有錯,但是當 MySQL 正準備要 UPDATE 的時候,可能已經有人把庫存扣成 0 了,但是程式卻渾然不知,將錯就錯的 UPDATE 下去了。

因此必須透過的交易機制來確保讀取及送交的資料都是正確的。

於是我們在 MySQL 就可以這樣測試: (註1)

SET AUTOCOMMIT=0;
BEGIN WORK;
SELECT quantity FROM products WHERE id=3 FOR UPDATE;
===========================================
此時 products 資料中 id=3 的資料被鎖住(註3),其它交易必須等待此次交易
送交後才能執行 SELECT * FROM products WHERE id=3 FOR UPDATE (註2)
如此可以確保 quantity 在別的交易讀到的數字是正確的。

===========================================
UPDATE products SET quantity = '1' WHERE id=3 ;
COMMIT WORK;
===========================================
送交(Commit)寫入資料庫,products 解鎖。


你可能感兴趣的:(oracle,sql,数据库,测试,input)