案例:Oracle 11g裏寫了一個存儲過程,內容爲執行update語句,更改表記錄狀態,目的是對錶記錄進行鎖定。應用服務器集羣調用同一個數據庫的這個存儲過程。最後發現,多個WebLogic實例都更新了同一條記錄。鎖機制失效。sql
進行代碼測試:數據庫
數據初始化準備服務器
create table tb_test_tab( id number(10), name varchar2(10), statu varchar2(10) ); truncate table tb_test_tab; insert into tb_test_tab values (1001,'1','1'); insert into tb_test_tab values (1002,'2','1'); insert into tb_test_tab values (1003,'3','1'); insert into tb_test_tab values (1004,'4','1'); insert into tb_test_tab values (1005,'5','1'); insert into tb_test_tab values (1006,'6','1'); commit;
在plsql客戶端開2個sql窗口,第一個窗口執行如下語句,不進行提交事務。測試
update tb_test_tab t set t.name='343333',t.statu='2' where t.statu='1' and t.id=1001;
第二個窗口同時執行如下語句,提交事務。spa
update tb_test_tab t set t.name='343333',t.statu='3' where t.statu='1' and t.id=1001; commit;
此時第二個語句的commit處於等待狀態,如今提交第一個窗口的事務,第二個事務也會當即提交。查看結果發現 t.id=1001記錄的 t.statu值爲2。說明表的鎖機制成功!code
如今換成嵌套語句做爲更新條件進行執行更新處理,看下鎖機制如何,操做步驟以下:事務
先初始化數據。it
truncate table tb_test_tab; insert into tb_test_tab values (1001,'1','1'); insert into tb_test_tab values (1002,'2','1'); insert into tb_test_tab values (1003,'3','1'); insert into tb_test_tab values (1004,'4','1'); insert into tb_test_tab values (1005,'5','1'); insert into tb_test_tab values (1006,'6','1'); commit;
一樣在plsql客戶端開2個sql窗口,第一個窗口執行如下語句,不進行提交事務。語句含義就是將statu='1'的記錄升序後的前三條設置statu='2',name='22222'。table
update tb_test_tab t set t.name = '22222', t.statu = '2' where t.id in (select id from (select r.id from tb_test_tab r where r.statu='1' order by r.id) where rownum <= 3);
第二個窗口同時執行如下語句,提交事務。class
update tb_test_tab t set t.name = '33333', t.statu = '3' where t.id in (select id from (select r.id from tb_test_tab r where r.statu='1' order by r.id) where rownum <= 3); commit;
此時第二個語句的commit處於等待狀態,如今提交第一個窗口的事務,第二個事務也會當即提交。查看結果發現id升序後的前三條記錄的 statu='3',name='33333'。 說明表的鎖機制存在異常,至少不是以前咱們想象的那樣的結果(以前想象的結果應該是 statu='2',name='22222'。 第二個窗口執行的語句更新記錄爲0條)! 這個也許就是Oracle內部鎖機制靈敏度不針對嵌套語句裏面的結果集。故須要謹慎使用嵌套語句做爲鎖機制的限制條件。
若是想上面嵌套語句不變,同時想讓鎖機制起到效果,能夠將嵌套語句裏面的限制條件在最外層再寫一遍便可。具體以下。第一個窗口改爲:
update tb_test_tab t set t.name = '22222', t.statu = '2' where t.id in (select id from (select r.id from tb_test_tab r where r.statu='1' order by r.id) where rownum <= 3) and t.statu='1';
第二個窗口改爲:
update tb_test_tab t set t.name = '33333', t.statu = '3' where t.id in (select id from (select r.id from tb_test_tab r where r.statu='1' order by r.id) where rownum <= 3) and t.statu='1'; commit;