天天看點

資料庫中的悲觀鎖和樂觀鎖和死鎖

http://blog.csdn.net/lemontreey/article/details/53067437

死鎖

所謂死鎖:是指兩個或兩個以上的程序在執行過程中,因争奪資源而造成的一種互相等待的現象,若無外力作用,它們都将無法推進下去。此時稱系統處于死鎖狀态或系統産生了死鎖,這些永遠在互相等待的程序稱為死鎖程序。由于資源占用是互斥的,當某個程序提出申請資源後,使得有關程序在無外力協助下,永遠配置設定不到必需的資源而無法繼續運作,這就産生了一種特殊現象死鎖。 一種情形,此時執行程式中兩個或多個線程發生永久堵塞(等待),每個線程都在等待被其他線程占用并堵塞了的資源。例如,如果線程A鎖住了記錄1并等待記錄2,而線程B鎖住了記錄2并等待記錄1,這樣兩個線程就發生了死鎖現象。計算機系統中,如果系統的資源配置設定政策不當,更常見的可能是程式員寫的程式有錯誤等,則會導緻程序因競争資源不當而産生死鎖的現象。鎖有多種實作方式,比如意向鎖,共享-排他鎖,鎖表,樹形協定,時間戳協定等等。鎖還有多種粒度,比如可以在表上加鎖,也可以在記錄上加鎖。

産生死鎖的原因主要是:

(1)系統資源不足。

(2) 程序運作推進的順序不合适。

(3)資源配置設定不當等。

如果系統資源充足,程序的資源請求都能夠得到滿足,死鎖出現的可能性就很低,否則就會因争奪有限的資源而陷入死鎖。其次,程序運作推進順序與速度不同,也可能産生死鎖。

産生死鎖的四個必要條件:

(1) 互斥條件:一個資源每次隻能被一個程序使用。

(2) 請求與保持條件:一個程序因請求資源而阻塞時,對已獲得的資源保持不放。

(3) 不剝奪條件:程序已獲得的資源,在末使用完之前,不能強行剝奪。

(4) 循環等待條件:若幹程序之間形成一種頭尾相接的循環等待資源關系。

這四個條件是死鎖的必要條件,隻要系統發生死鎖,這些條件必然成立,而隻要上述條件之一不滿足,就不會發生死鎖。

悲觀鎖

當我們使用悲觀鎖的時候我們首先必須關閉mysql資料庫的自動送出屬性,因為MySQL預設使用autocommit模式,也就是說,當你執行一個更新操作後,MySQL會立刻将結果進行送出。

關閉指令為:set autocommit=0;

悲觀鎖可以使用select…for update實作,在執行的時候會鎖定資料,雖然會鎖定資料,但是不影響其他事務的普通查詢使用。此處說普通查詢就是平時我們用的:select * from table 語句。在我們使用悲觀鎖的時候事務中的語句例如:

//開始事務

begin;/begin work;/start transaction; (三選一)

//查詢資訊

select * from order where id=1 for update;

//修改資訊

update order set name=’names’;

//送出事務

commit;/commit work;(二選一)

此處的查詢語句for update關鍵字,在事務中隻有SELECT … FOR UPDATE 或LOCK IN SHARE MODE 同一條資料時會等待其它事務結束後才執行,一般的SELECT查詢則不受影響。

執行事務時關鍵字select…for update會鎖定資料,防止其他事務更改資料。但是鎖定資料也是有規則的。

查詢條件與鎖定範圍:

1、具體的主鍵值為查詢條件

比如查詢條件為主鍵ID=1等等,如果此條資料存在,則鎖定目前行資料,如果不存在,則不鎖定。

2、不具體的主鍵值為查詢條件

比如查詢條件為主鍵ID>1等等,此時會鎖定整張資料表。

3、查詢條件中無主鍵

會鎖定整張資料表。

4、如果查詢條件中使用了索引為查詢條件

明确指定索引并且查到,則鎖定整條資料。如果找不到指定索引資料,則不加鎖。

悲觀鎖的確定了資料的安全性,在資料被操作的時候鎖定資料不被通路,但是這樣會帶來很大的性能問題。是以悲觀鎖在實際開發中使用是相對比較少的。

樂觀鎖

相對悲觀鎖而言,樂觀鎖假設資料一般情況下不會造成沖突,是以在資料進行送出更新的時候,才會對資料的沖突與否進行檢測,如果發現沖突,則讓傳回使用者錯誤的資訊,讓使用者決定如何去做。

一般來說,實作樂觀鎖的方法是在資料表中增加一個version字段,每當資料更新的時候這個字段執行加1操作。這樣當資料更改的時候,另外一個事務通路此條資料進行更改的話就會操作失敗,進而避免了并發操作錯誤。當然,還可以将version字段改為時間戳,不過原理都是一樣的。

例如有表student,字段:

id,name,version

1 a 1

當事務一進行更新操作:update student set name=’ygz’ where id = #{id} and version = #{version};

此時操作完後資料會變為id = 1,name = ygz,version = 2,當另外一個事務二同樣執行更新操作的時候,卻發現version != 1,此時事務二就會操作失敗,進而保證了資料的正确性。

悲觀鎖和樂觀鎖都是要根據具體業務來選擇使用,本文僅作簡單介紹。

總結

悲觀鎖會鎖定資料,其他操作不會影響到被鎖的資料,但是普通的查詢沒有影響,需要用到 for update語句

實作樂觀鎖的方法是在資料表中增加一個version字段,每當資料更新的時候這個字段執行加1操作。這樣當資料更改的時候,另外一個事務通路此條資料進行更改的話就會操作失敗,進而避免了并發操作錯誤。