1. 程式人生 > >數據庫(六),鎖

數據庫(六),鎖

方向 strong strip 最好 修復 還需要 下一步 nsh view

在數據庫(五),事務裏面我們講了事務ACID屬性,事務最重要的能在異常情況的修復以及並發連接的處理上。

異常情況的修復主要通過日誌來完成,那麽並發連接的處理主要通過。本章主要整理的是的相關知識。

為什麽需要鎖?

現在Bob的賬戶裏面有1000塊錢,此時程序突然同時來了兩個要求,一個要把Bob的錢轉給Smith 20塊,一個要把Bob的錢轉Joe 30塊。這兩個要求一查Bob的賬戶,都發現現在Bob有1000塊,所以要求A算出現在Bob應該有980塊,要求B算出來Bob應有970。要求A的數據被要求B的數據覆蓋了。

這樣就出問題了,明明應該扣50塊錢,現在卻只是扣了30塊。

就是用來解決這樣的並發訪問的問題。當每次訪問Bob賬戶之前,都加一個鎖,禁止別人再次訪問,只有等待持有鎖的人來釋放

技術分享圖片

悲觀鎖和樂觀鎖

悲觀鎖

如果事務A把Bob賬戶鎖住了,事務B自然不能操作Bob賬戶,也就是說其他線程只能在外面等待。

這種加鎖的方式就是悲觀鎖。它每次取讀寫數據時總認為數據會被別人修改,所以將數據加鎖,置於鎖定狀態,不讓別人訪問。

缺點是如果持有鎖的時間太長,其他用戶需要等待很長的時間。

悲觀鎖主要適用於並發爭搶比較嚴重的場景。

樂觀鎖

悲觀鎖的問題顯而易見,如果將數據加鎖了以後,其他的線程是無法訪問的,只能等待。如果持有鎖的時間太長,需要等待大量的時間。

所以我們引入了樂觀鎖,所謂樂觀鎖是認為一般情況下不會有太多的人修改余額,所有沒有加鎖,只有在最後更新的時候才去看是否有沖突。

具體怎麽做呢?

可以在日誌中加上一個version(版本)字段,

  • 每次的時候,不僅需要讀出余額,還需要讀出版本號。

  • 等修改了余額以後,往回寫之前需要檢查一下版本號,看看與讀的時候版本號是否一樣。
    • 如果不一樣,說明數據已經被改變了,所以需要放棄寫操作,重新讀取余額和版本號

    • 如果一樣,則將新余額寫回去,把版本號加1 。

比如

事務1把Bob的余額減去30,此時它讀到了(Bob余額=1000,版本=1)

事務2也需要將Bob的余額減去50,他也讀到了(Bob余額=1000,版本=1)

然後事務1率先完成計算,把新的余額值970寫回了,版本 加 1 ,變成了版本2。

事務2寫回去的時候,發現最新的版本號變為2,表示之前讀的數據已經改變,所以需要重新讀一遍

這就是樂觀鎖,這種方式適合於沖突不多的場景,如果沖突很多,數據爭用激烈,會導致不斷的嘗試,反而降低了性能。
技術分享圖片

死鎖

死鎖產生的條件

如果出現如下這種情況

  • 有兩個線程同時參與

  • 這兩個線程在不同方向給同一個資源加鎖

  • 爭搶相同的資源

那麽很可能出現死鎖

比如事務1是Bob給Smith轉賬,事務2是Smith給Bob轉賬。

當這兩個事務單元同時發生的時候,就有問題呢。

事務單元1會先鎖定Bob,然後鎖定Smith,而事務單元2會先鎖定Smith,然後鎖定Bob

事務1會等待事務2把Bob給釋放了,而事務2在等待事務1把Smith釋放了。

技術分享圖片

如何解決

那麽如何解決死鎖呢?最好的方法是盡可能不出現死鎖,當然很難。或者說如果鎖定時間超時了,則強行釋放,不過這種方法效率比較低,因為如果有用戶的事務本來時間就很長,則每個死鎖的檢測時間將會很長。

所以最優的方案在於預測死鎖,可以把事務單元等待的鎖記錄下來

比如下圖中,事務單元1持有"Lock Bob"的鎖,現在又在申請一把"Lock Smith"的鎖,在申請之前,可以查看同樣申請了"Lock Smith"的有哪些事務單元。明顯事務單元2也申請過這把鎖。好了,下一步是看事務單元2在申請什麽鎖呢,發現它居然在申請"Lock Bob"這把鎖,而這把鎖目前由事務單元1持有。所以現在已經發現有死鎖的可能了,也就是發生了碰撞。所以可以提前補救。

技術分享圖片

U鎖

下面來討論一種死鎖的情況。如下圖
技術分享圖片

事務1 Trx1

開始事務1
讀A(讀鎖)
A - 100(讀鎖需要升級為寫鎖)
提交事務1

事務2 Trx2

開始事務2
讀A(讀鎖)
A - 100(讀鎖需要升級為寫鎖)
提交事務2(解鎖)

事務1和事務2的讀鎖是可以並行的,所以讀鎖可以同時進入臨界區,但是寫鎖不能,會被擋在外面。此時事務2又發起了寫鎖。那麽尷尬的局面就產生了。

事務1的寫鎖需要等事務2的讀鎖釋放資源。

事務2的寫鎖需要等待事務1的讀鎖釋放資源。

所以形成了死鎖。其實這種死鎖的形成條件非常的簡單,只需要針對同一個數據進行讀寫。比如說update set A=A-1 where id = 100如果運行多次,就會出現死鎖

解決的辦法是引入U鎖,可以將讀鎖直接升級為寫鎖。

對於事務1,讀以後馬上就是寫,所以直接就使用寫鎖,而不是讀鎖呢。
技術分享圖片

同理事務2也是如此。

技術分享圖片

技術分享圖片

數據庫(六),鎖