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 解鎖。