數據庫鎖機制及樂觀鎖,悲觀鎖的併發控制

1.數據庫鎖的種類mysql

  ① 共享鎖程序員

    共享鎖是在執行select操做時使用的鎖機制.web

      共享鎖與共享鎖共存,即當一個事務正在對A表進行查詢操做時,另外一個事務一樣能夠對A表進行查詢操做,演示以下:sql

1 T1:  select * from A;(加共享鎖A)
2 T2:  select * from A;(加共享鎖B)
3 -- 此時T1,T2可同時執行

  ② 排它鎖

    排它鎖是在執行update,delete等對數據有修改操做時使用的鎖.數據庫

    排它鎖不與排它鎖以及共享鎖共存,即當一個事務正在對A表進行更新操做時,會對操做的數據加上排它鎖,其餘事務沒法訪問更沒法修改被加鎖的數據行,演示以下:安全

1 T1: update A set a=1 where id<100;
2 T2: update A set a=2 where id>100;
3 T3: update A set a=3 where id=50;
4 T4: select * from A where id<50;

    當T1先到達時,會對id<100的數據加上排它鎖,其餘事務沒法訪問這些數據,因爲T2訪問的是id>100的數據,改數據未被加上排它鎖,因此T2可以對id>100的數據加排它鎖,session

    可是T3訪問的數據已經被T1加上排它鎖,除非T1執行完畢釋放排它鎖,T3才能訪問id=50的數據.T4跟T3同理.併發

    排它鎖的併發性低下,請看下面這個例子:spa

1 T1:update table set column1='hello' where id=10
2 T2:update table set column1='world' where id=20

    該例子有兩種狀況:code

    第一種狀況:若是id是主鍵(有主鍵索引)或者有索引的一列,T1會一會兒找到id=10的那一列,並對該列加上排它鎖.T2同理,這兩個事務互不干涉,可以併發執行

    第二種狀況:若是id是普通的列,且沒有索引,那麼當T1搜索到id=10這一列並對他加上排它鎖以後,因爲T2須要全表掃描找到id=20這一列,可是因爲T1對id=10這一列加上了排它鎖,T2沒法訪問,就會進入阻塞狀態,試想若是T1是一個較大的事務,那麼T2一直得不到想要的資源,嚴重影響用戶體驗,尤爲是在web應用這種對程序的效率要求較高的應用.

    排它鎖的併發性低下,可是更新鎖可以比較好的解決這個問題.

  ③ 更新鎖

     被加上更新鎖的數據是可能被修改的數據.更新鎖與排它鎖及更新鎖互斥,與共享鎖共存.演示以下:

1 T1: select * from A;(加更新鎖)
2 T2: select * from A;(加更新鎖)
3 T3: select * from A;(加共享鎖)
4 T4: update A set a=1;(加排它鎖)

    當T1到達時,對A表加更新鎖,而後T2到達,企圖對A表加更新鎖,可是發現表A已經被加上更新鎖,其阻塞.而後T3到達,對A表加共享鎖,因爲共享鎖與更新鎖共存,因此T3可以加上共享鎖並可以正常讀取,而後T4到達,因爲排它鎖與更新鎖互斥,因此T4阻塞.如下是另外一個例子:

1 T1:
2 select * from A;(加更新鎖)
3 update set a=1 where id=1;
4 T2:
5 select * from A;(加共享鎖)
6 update set a=1 where id=1;

    T1先到達執行查詢操做,同時T2到達,T2開始查詢操做,二者能夠共存.

    第一種狀況:假設T2查詢操做先結束,準備執行更新操做對相應行上鎖時,發現該表有了更新鎖,因此該操做阻塞.當T1查詢操做結束後,執行更新操做,更新鎖自動升級爲排它鎖,T1更新結束以後,釋放鎖,T2開始更新.

    第二種狀況:假設T1查詢操做先結束,準備執行更新操做,更新鎖升級爲排它鎖,因爲排它鎖與共享鎖不一樣存,T2的查詢操做被阻塞,等T1的更新操做結束以後,T2才能繼續操做.

  ④ 意向鎖

     意向鎖就是反應當前表是否有鎖的鎖機制.

     若是T1正在對A表的一部分數據進行更新操做(加了行級鎖),這個時候另外一個事務T2須要對A表加表級鎖,因爲表若是存在行級鎖以後沒法再添加表級鎖,它會對全表進行掃描判斷表的每一行是否存在排它鎖,這樣效率低下.意向鎖就會可以防止這種狀況的發生,當A表有一行被加上了排它鎖以後,A表會自動被加上意向排它鎖,那麼其餘事務須要加表級鎖的事務只須要判斷A表是否有意向鎖就好了.

1 T1: update A set a=1 where id=1;(加行級排它鎖)
2 T2: update A set a=1 where id=1;(加表級排它鎖)

    當T1執行更新操做時,對id=1這一行加上行級鎖以後,數據庫會自動對A表加上一個意向排它鎖,當T2到達以後,T2會判斷A表是否有意向排它鎖,若是存在則阻塞,不存在則對A表加上表級排他鎖.

     ⑤ 小tips

    在操做數據庫時,數據庫會自動爲操做的數據添加合適的鎖,這就是爲何不須要懂得數據庫的鎖機制咱們依然能夠愉快的寫SQL.不一樣的存儲引擎支持的鎖機制不一樣,根據須要可選擇不一樣的存儲引擎.

    程序員可手動對對象加鎖,這裏以我熟悉的mysql數據庫爲例:這裏我使用的是mysql默認的innodb存儲引擎,該引擎支持行級鎖,表級鎖.InnoDB行鎖是經過給索引上的索引項加鎖來實現的,若是不存在索引,則對整張表加鎖.

    添加表級鎖以下:      

1 LOCK table admin write; -- 對admin加表鎖
2 unlock tables;-- 釋放當前session具備的表鎖

      對錶添加了寫鎖以後,是沒法對其餘表進行操做的,全部事務必須在一開始獲取本身須要的全部鎖,獲取了寫鎖以後能進行寫與讀操做,獲取了讀鎖以後,只能進行讀操做

     添加行級鎖以下:

1 select * from admin for update; -- 得到排它鎖

    更多關於mysql的鎖機制請自行看官方文檔.

2.樂觀鎖與悲觀鎖

  樂觀鎖與悲觀鎖是爲了保持事務的隔離性以及數據庫的一致性而被業內定義出來的一種手段,並不屬於數據庫鎖機制,可是悲觀鎖是依賴於數據庫的鎖機制實現的.

    ① 樂觀鎖

     在關係數據庫管理系統裏,樂觀併發控制(又名「樂觀鎖」,Optimistic Concurrency Control,縮寫「OCC」)是一種併發控制的方法。它假設多用戶併發的事務在處理時不會彼此互相影響,各事務可以在不產生鎖的狀況下處理各自影響的那部分數據。在提交數據更新以前,每一個事務會先檢查在該事務讀取數據後,有沒有其餘事務又修改了該數據。若是其餘事務有更新的話,正在提交的事務會進行回滾。

    樂觀鎖認爲其餘事務都不會修改本身的正在操做的數據,因此不會使用數據鎖機制(數據庫鎖機制沒法關閉,這裏說的不使用是不會像悲觀鎖那樣顯示加鎖,悲觀鎖操做見第4點)..

    ② 悲觀鎖

    在關係數據庫管理系統裏,悲觀併發控制(又名「悲觀鎖」,Pessimistic Concurrency Control,縮寫「PCC」)是一種併發控制的方法。它能夠阻止一個事務以影響其餘用戶的方式來修改數據。若是一個事務執行的操做都某行數據應用了鎖,那只有當這個事務把鎖釋放,其餘事務纔可以執行與該鎖衝突的操做。
      悲觀併發控制主要用於數據爭用激烈的環境,以及發生併發衝突時使用鎖保護數據的成本要低於回滾事務的成本的環境中。

      悲觀鎖的實現主要依賴於數據庫的鎖機制,會顯示的使用鎖.

3.樂觀鎖併發控制

  樂觀鎖併發控制經常使用方法是使用一個版本號控制手段.即在表中添加一個版本號version字段,當有一個事務對該數據進行操做時會取得版本號,當事務對數據更新時會判斷數據庫中的version是否跟本身取得version一致,一致則說明該數據未被修改過,不然說明數據被修改過,更新操做失敗.更新成功則對version字段加一.

T1: 
select * from A;
update A set a=1,version=version+1 where id=1 and version=vs;
T2:
select * from A;
update A set a=2,version=version+1 where id=1 and version=vs;
-- vs爲事務取得的版本號

    假設T1,T2併發的對A表進行操做,T1,T2取得的version字段值相同,T1事務先執行完,T1執行完以後version被加一,T2在提交更新時,由於version字段已經被修改,因此更新失敗.

    樂觀鎖不適用於更新操做較多的狀況,若是更新操做較多,會出現不少更新失敗的狀況,須要大量回滾,從新操做,程序效率大幅度下降.可是他的查詢併發度高.

4.悲觀鎖併發控制

  悲觀鎖併發控制主要經過對操做的數據加鎖實現.下面這個例子是經過加排它鎖實現:

select * from A where id=1 for update

  在操做A表示,對id=1的行加上行級鎖,其餘事務沒法操做該數據.實現了悲觀鎖.

  悲觀鎖併發度低,每次都須要對錶上鎖,可是可以保證數據的安全.

相關文章
相關標籤/搜索