資料庫的鎖機制

2021-08-08 22:49:40 字數 3342 閱讀 2943

事務是併發控制的基本單位,保證事務acid原則是事務處理的重要任務,但是當多個事務對資料庫進行併發操作時,就有可能破壞事務的acid特性。

為了保證事務的隔離性與一致性,就有了資料庫的鎖機制。

在資料庫中,存在著很多種類的鎖:共享鎖、排他鎖、悲觀鎖、樂觀鎖、行級鎖、表級鎖等。

鎖有兩種基本的型別:共享鎖、排他鎖

共享鎖(share locks,簡稱s鎖,也叫讀鎖)

若事務t對資料物件a加上s鎖,則事務t只能讀取a而不能修改a,其他事務也只能對該資料物件a加上s鎖,而不能加上x鎖,直到事務t釋放a上的s鎖。

即是說,共享鎖只允許多個事務讀取資料,而不允許修改資料。

排他鎖(exclusive locks,簡稱x鎖,也叫寫鎖)

若事務t對資料物件a加上x鎖,則只允許事務t對a進行讀取和修改,其他事務都不能對a加上任何型別的鎖,直到事務t釋放a上的x鎖。

即是說,排他鎖只允許乙個事務讀取和修改被鎖定的資料。

給資料加鎖可能會引起活鎖和死鎖的問題。

活鎖

如果事務t1封鎖了資料物件r,事務t2也請求封鎖r,於是t2等待。t3也請求封鎖r,當t1釋放了r上的鎖後系統首先批准了t3的請求,於是t2仍然等待。然後t4也請求封鎖r,當t3釋放了r上的鎖後系統又批准了t4的請求,於是t2仍然等待……

在這個過程中,t2可能會永遠在等待,這就是活鎖。

避免活鎖的簡單方法就是採用先來先到的策略,當多個事務請求封鎖同一資料物件的時候,封鎖子系統按請求封鎖的先後次序對事務進行排隊,資料物件上的鎖一旦釋放,就批准申請佇列中第乙個事務獲得鎖。

死鎖

如果事務t1封鎖了資料r1,事務t2封鎖了資料r2,然後t1請求封鎖r2,由於r2已經被t2封鎖,所以t1等待t2釋放r2上的鎖;接著t2也請求封鎖r1,由於r1已經被t1封鎖,所以t2等待t1釋放r1上的鎖。

這樣就出現了兩個事務互相等待的局面,這兩個事務永遠也不能結束,形成了死鎖。

資料庫一般允許發生死鎖,並採用一定手段定期診斷系統中有無死鎖,若有則解除之。一般採用超時法或事務等待圖法來診斷死鎖。

如果乙個事務等待的時間超過了規定的時限,就認為發生了死鎖。超時法實現簡單,但有可能發生誤判死鎖,事務因為其他原因使等待時間超過了時限導致被系統誤認為發生了死鎖;如果時限設定得太長,也可能無法及時發現死鎖。

事物等待圖是乙個有向圖g=(t,u)。t為結點的集合,每個結點表示正執行的事務;u為邊的集合,每條邊表示事務等待的情況。若t1等待t2,則t1、t2之間畫一條有向邊,從t1指向t2。

事務等待圖動態地反映了所有事務的等待情況,併發控制子系統周期性地生成事務等待圖,並進行檢測。如果發現圖中存在迴路,則表示系統中發生了死鎖。

死鎖的情況可以多種多樣,如下圖所示,在大迴路中又有小迴路。

如何解除死鎖

當dbms檢測到系統中存在死鎖,就要設法解除。通常採用的做法是選擇乙個處理死鎖代價最小的事務,將其撤銷,釋放此事務持有的鎖,使其他事務得以執行下去。解除死鎖之後,被撤銷的事務所執行的資料操作必須加以恢復。

鎖的粒度就是鎖的作用範圍,一般分為行級鎖、表級鎖。行級鎖鎖定記錄行,表級鎖鎖定整個表。

行級鎖

系統開銷大,加鎖慢,鎖定粒度最小,會發生死鎖,但是發生衝突的概率最低,併發性最高

表級鎖

系統開銷小,加鎖快,鎖定粒度最大,不會發生死鎖,但是發生衝突的概率最高,併發性最低

總結

顧名思義,就是很悲觀,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會block直到它拿到鎖。傳統的關係型資料庫裡邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。

它指的是對資料被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個資料處理過程中,將資料處於鎖定狀態。

悲觀鎖的實現,往往依靠資料庫提供的鎖機制(也只有資料庫層提供的鎖機制才能真正保證資料訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改資料)。

顧名思義,就是很樂觀,每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個資料。

樂觀鎖適用於多讀的應用型別,即衝突真的很少發生的時候,這樣可以提高吞吐量。但如果經常產生衝突,上層應用會不斷的進行retry,這樣反倒會降低效能,所以這種情況下用悲觀鎖就比較合適。

樂觀鎖與悲觀鎖不同的是,它是一種邏輯上的鎖,而不需要資料庫提供鎖機制來支援,它需要我們自己在程式中實現。樂觀鎖的實現,一般是通過資料版本version或者時間戳來實現。

資料版本version(版本戳)

在需要樂觀鎖的表中新增乙個version欄位,每修改一次資料就將version+1。每次對資料進行操作時先讀出當前的資料版本,如果要對資料進行修改就先判斷此時的version是否與之前查詢到的version一致,如果version一樣說明在這期間沒有其他人修改這條資料,則可以執行此次更新操作並更新版本號;如果version不一致,則意味著衝突,不執行此次更新。

**實現例子:

查詢當前商品資訊:

select name,num,version from products where id = #;

執行更新商品操作:

update products set num = num - 1, version = version + 1 where id = # and version = #;

時間戳timestamp

和version類似,每操作一次資料就修改時間戳;在進行更新操作時需要先判斷當前時間戳是否與之前查詢到的時間戳一致。

總結

通常情況下,寫操作較少時,使用樂觀鎖,寫操作較多時,使用悲觀鎖。除了自己手動實現樂觀鎖之外,有的持久層框架已經封裝好了樂觀鎖的實現。比如hibernate就提供了以資料版本實現的樂觀鎖機制。

資料庫鎖機制

這段時間由於開發專案,重新學習了資料庫的併發控制和鎖機制。資料庫就是通過鎖機制來解決併發問題的。主要就是兩種鎖,共享鎖和排他鎖 也叫獨佔鎖 在執行select語句的時候需要給操作物件 表或者一些記錄 加上共享鎖,但加鎖之前需要檢查是否有排他鎖,如果沒有,則可以加共享鎖 乙個物件上可以加n個共享鎖 否...

資料庫鎖機制

這段時間由於開發專案,重新學習了資料庫的併發控制和鎖機制。資料庫就是通過鎖機制來解決併發問題的。主要就是兩種鎖,共享鎖和排他鎖 也叫獨佔鎖 在執行select語句的時候需要給操作物件 表或者一些記錄 加上共享鎖,但加鎖之前需要檢查是否有排他鎖,如果沒有,則可以加共享鎖 乙個物件上可以加n個共享鎖 否...

資料庫鎖機制

資料庫就是通過鎖機制來解決併發問題的。主要就是兩種鎖,共享鎖和排他鎖 也叫獨佔鎖 在執行select語句的時候需要給操作物件 表或者一些記錄 加上共享鎖,但加鎖之前需要檢查是否有排他鎖,如果沒有,則可以加共享鎖 乙個物件上可以加n個共享鎖 否則不行。共享鎖通常在執行完select語句之後被釋放,當然...