【鎖機制】共享鎖、排它鎖、悲觀鎖、樂觀鎖、死鎖等等

不一樣的天空w發表於2016-10-22

鎖是防止在兩個事務操作同一個資料來源(表或行)時互動破壞資料的一種機制。Oracle採用封鎖技術保證併發操作的可序列性。Oracle的鎖分為兩大類:資料鎖(也稱DML鎖)和字典鎖。字典鎖是Oracle DBMS內部用於對字典表的封鎖。字典鎖包括語法分析鎖和DDL鎖,由DBMS在必要的時候自動加鎖和釋放鎖,使用者無機控制。

Oracle主要提供了5種資料鎖:共享鎖(Share Table Lock,簡稱S鎖)、排它鎖(Exclusive Table Lock,簡稱X鎖)、行級鎖(Row Share Table Lock,簡稱RS鎖)、行級排它鎖(Row Exclusive Table Lock,簡稱RX鎖)和共享行級排它鎖(Share Row Exclusive Table Lock,簡稱SRX鎖)。其封鎖粒度包括行級和表級。

按照型別劃分,鎖可分為行級排他鎖和表級共享鎖及死鎖。按照物件劃分,鎖可分為事務鎖,DML鎖,DDL鎖,記憶體鎖(Latch)。


鎖相關測試

1.1. 更新丟失

1)   建立測試表

SYS@ORA11GR2>create table t_lock as select rownum as id,0 as type from dual connect by rownum <=3;

 

Table created.

 

SYS@ORA11GR2>select * from t_lock;

 

        ID       TYPE

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

         1          0

         2          0

         3          0

 

2)   會話1:查詢type0的最小id

SYS@ORA11GR2>set time on;

18:58:22 SYS@ORA11GR2>

18:58:23 SYS@ORA11GR2>select min(id) from t_lock where type=0;

 

   MIN(ID)

----------

         1

 

3)   會話2:查詢type0的最小id

SYS@ORA11GR2>set time on

18:59:31 SYS@ORA11GR2>select min(id) from t_lock where type=0;

 

   MIN(ID)

----------

         1

 

4)   會話1:將ID1的這條記錄的type置為1

19:00:53 SYS@ORA11GR2>update t_lock set type=1 where id=1;

1 row updated.

 

19:01:21 SYS@ORA11GR2>commit;

 

Commit complete.

 

19:01:37 SYS@ORA11GR2>select * from t_lock;

 

        ID       TYPE

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

         1          1

         2          0

         3          0

 

5)   會話2:將ID1的這條記錄的type置為2

19:02:47 SYS@ORA11GR2>update t_lock set type=2 where id=1;

 

1 row updated.

 

19:03:11 SYS@ORA11GR2>commit;

 

Commit complete.

 

19:03:17 SYS@ORA11GR2>select * from t_lock;

 

        ID       TYPE

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

         1          2

         2          0

         3          0

 

19:03:32 SYS@ORA11GR2>

6)   小結:

我們看到id1type現在的值為2,會話1type更新為1的記錄已經“丟失”

1.2. 悲觀鎖

1)   會話1查詢id2的記錄並進行鎖定

19:05:43 SYS@ORA11GR2>select * from t_lock where id=2 and type =0 for update nowait;

 

        ID       TYPE

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

         2          0

 

2)   會話2:查詢id2的記錄,此時查詢報錯

19:07:43 SYS@ORA11GR2>select * from t_lock where id=2 and type=0 for update nowait;

select * from t_lock where id=2 and type=0 for update nowait

              *

ERROR at line 1:

ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired

 

3)   會話1:對id2的記錄進行更新。

19:19:08 SYS@ORA11GR2>update t_lock set type=1 where id=2 and type=0;

 

1 row updated.

 

19:19:30 SYS@ORA11GR2>commit;

 

Commit complete.

 

19:19:39 SYS@ORA11GR2>select * from t_lock where id=2;

 

        ID       TYPE

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

         2          1

 

4)   會話2查詢id2的記錄,由於已經將id2type已經變為1,所以查不到資料了。

19:19:15 SYS@ORA11GR2>select * from t_lock where id=2 and type=0 for update nowait;

 

no rows selected

 

1.3樂觀鎖

1)   會話1:查詢id3的偽列ora_rowscn的值

19:22:00 SYS@ORA11GR2>select id,type,ora_rowscn from t_lock where id = 3;

 

        ID       TYPE ORA_ROWSCN

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

         3          0    1246809

 

2)   會話2:查詢id3的偽列ora_rowscn的值

19:23:01 SYS@ORA11GR2>select id,type,ora_rowscn from t_lock where id = 3;

 

        ID       TYPE ORA_ROWSCN

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

         3          0    1246809

 

3)   會話1:更新id3type1

19:24:22 SYS@ORA11GR2>update t_lock set type=1 where ora_rowscn=1246809 and id = 3;

 

1 row updated.

 

19:25:29 SYS@ORA11GR2>commit;

 

Commit complete.

 

驗證:

19:28:22 SYS@ORA11GR2>select id,type,ora_rowscn from t_lock where id = 3;

 

        ID       TYPE ORA_ROWSCN

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

         3          1    1247164

 

4)   會話2:更新id3type1

19:26:05 SYS@ORA11GR2>update t_lock set type=1 where ora_rowscn=1246809 and id =3;

 

0 rows updated.

 

驗證:

19:29:37 SYS@ORA11GR2>select id,type,ora_rowscn from t_lock where id = 3;

 

        ID       TYPE ORA_ROWSCN

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

         3          1    1247164

(因為會話1的事務更改了id=3的值,而且事務已經提交,事務的ora_rowscn已經變為1247164,原來的ora_rowscn=1246809已經不存在,所以沒有可更改的行了)

1.4死鎖

1)   建立測試表

19:35:46 SYS@ORA11GR2>create table t_lock_1 (id number(2),name varchar2(15));

 

Table created.

 

19:35:57 SYS@ORA11GR2>create table t_lock_2 as select * from t_lock_1;

 

Table created.

 

19:36:24 SYS@ORA11GR2>insert into t_lock_1 values(1,'liubei');

 

1 row created.

 

19:37:11 SYS@ORA11GR2>insert into t_lock_2 values (1,'guanyu');

 

1 row created.

 

19:37:38 SYS@ORA11GR2>commit;

 

Commit complete.

 

19:37:43 SYS@ORA11GR2>select * from t_lock_1;

 

        ID NAME

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

         1 liubei

 

19:38:01 SYS@ORA11GR2>select * from t_lock_2;

 

        ID NAME

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

         1 guanyu

 

2)   會話1:更新表t_lock_1id欄位為1name為“liuxuande,不提交

19:39:55 SYS@ORA11GR2>update t_lock_1 set name='liuxuande' where id =1;

 

1 row updated.

3)   會話2:更新表t_lock_2id欄位為1name為“關雲長”,不提交

19:39:47 SYS@ORA11GR2>update t_lock_2 set name='guanyunchang' where id = 1;

 

1 row updated.

 

4)   會話1:更新表t_lock_2id欄位為1name為“guanyunchang”,此時掛起狀態

19:40:30 SYS@ORA11GR2>update t_lock_2 set name='guanyunchang' where id =1;

 

5)   會話2:更新表t_lock_1id欄位為1name為“liuxuande”,此時掛起狀態

19:44:14 SYS@ORA11GR2>update t_lock_1 set name='liuxuande' where id =1;

 

6)   會話1:此時回到會話1,出現死鎖錯誤

19:40:30 SYS@ORA11GR2>update t_lock_2 set name='guanyunchang' where id =1;

update t_lock_2 set name='guanyunchang' where id =1

       *

ERROR at line 1:

ORA-00060: deadlock detected while waiting for resource

 

會話1處於死鎖狀態,而會話2處於掛起狀態;

總結:悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會block直到它拿到鎖。
     樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿資料的時候都認為別人不會修改,所以不會上鎖。
     樂觀鎖適用於寫比較少的情況下,即衝突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果經常產生衝突,上層應用會不斷的進行retry,這樣反倒是降低了效能,所以這種情況下用悲觀鎖就比較合適。
     定義:當兩個使用者希望持有對方的資源時就會發生死鎖.
即兩個使用者互相等待對方釋放資源時,oracle認定為產生了死鎖,在這種情況下,將以犧牲一個使用者作為代價,另一個使用者繼續執行,犧牲的使用者的事務將回滾.
     起因:Oracle的死鎖問題實際上很少見,如果發生,基本上都是不正確的程式設計造成的,經過調整後,基本上都會避免死鎖的發生。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31397003/viewspace-2126924/,如需轉載,請註明出處,否則將追究法律責任。

相關文章