天天看點

面試必備的資料庫悲觀鎖與樂觀鎖

面試必備的資料庫悲觀鎖與樂觀鎖

前言

在上一個章節5分鐘帶你讀懂事務隔離性與隔離級别 的最後,其實我們已經提到了鎖的概念。本章節接下來将主要介紹以下資料庫

悲觀鎖與樂觀鎖

的相關知識。如有錯誤還請大家及時指出~

本文已同步至 GitHub/Gitee/公衆号,感興趣的同學幫忙點波關注~

問題:

  • 為什麼需要鎖?
  • 什麼是悲觀鎖?
  • 什麼是樂觀鎖?
  • 悲觀鎖與樂觀鎖差別與聯系?
  • 悲觀鎖與樂觀鎖的使用場景?

在并發環境下,如果多個用戶端通路同一條資料,此時就會産生資料不一緻的問題,如何解決,通過加鎖的機制,常見的有兩種鎖,樂觀鎖和悲觀鎖,可以在一定程度上解決并發通路。

1. 悲觀鎖(Pessimistic Lock)

1.1 定義

百度百科:

悲觀鎖,正如其名,具有強烈的獨占和排他特性。它指的是對資料被外界(包括本系統目前的其他事務,以及來自外部系統的事務處理)修改持保守态度,是以,在整個資料處理過程中,将資料處于鎖定狀态。悲觀鎖的實作,往往依靠資料庫提供的鎖機制(也隻有資料庫層提供的鎖機制才能真正保證資料通路的排他性,否則,即使在本系統中實作了加鎖機制,也無法保證外部系統不會修改資料)。

其他知識點

悲觀鎖主要是

共享鎖

排他鎖

共享鎖

又稱為讀鎖,簡稱S鎖,顧名思義,共享鎖就是多個事務對于同一資料可以共享一把鎖,都能通路到資料,但是隻能讀不能修改。

排他鎖

又稱為寫鎖,簡稱X鎖,顧名思義,排他鎖就是不能與其他所并存,如一個事務擷取了一個資料行的排他鎖,其他事務就不能再擷取該行的其他鎖,包括共享鎖和排他鎖,但是擷取排他鎖的事務是可以對資料就行讀取和修改。

1.2 案例分析

使用場景舉例:以MySQL InnoDB為例

作為示範,我們繼續使用之前的資料庫表:product表

productId productName productPrice productCount
1 小米 1999 100
2 魅族

首先我們需要

set autocommit=0

,即不允許自動送出

有看過上一篇文章5分鐘帶你讀懂事務隔離性與隔離級别 的同學,可以看到最後我們使用事務隔離級别時,所引申出來的根本問題就是可以通過鎖機制解決。

問題

在并發情況下回導緻資料一緻性的問題:

如果有A、B兩個使用者需要搶productId =1的小米手機,A、B使用者都查詢小米手機數量是100,A購買後修改商品的數量為99,B購買後修改數量為99。

用法

每次擷取小米手機時,對該商品加排他鎖。也就是在使用者A擷取擷取 id=1 的小米手機資訊時對該行記錄加鎖,期間其他使用者阻塞等待通路該記錄。代碼如下:

start transaction;
 
select p.productCount from product p where p.productId = 1 for update;
 
update product p set p.productCount=p.productCount-1 where p.productId=1 ;
 
commit;

           

操作

下面同時打開兩個視窗模拟2個使用者并發通路資料庫

時間軸 事務A 事務B
T1 start transaction;
T2 select p.productCount from product p where p.productId = 1 for update;
T3
T4 select p.productCount from product p where p.productId = 1 for update;(等待中...)

流程說明

  1. 使用者A start transaction開啟一個事物。前一步我們關閉了mysql的autocommit,是以需要手動控制事務的送出。
  2. 在獲得小米手機資訊(productId = 1 )時,進行資料加鎖操作(for update)。與普通查詢方式不同,我們使用了

    select…for update

    的方式,這樣就通過資料庫實作了悲觀鎖。在這個update事務送出之前其他外界是不能修改這條資料的,但是這種處理方式效率比較低,一般不推薦使用。
  3. 使用者B start transaction開啟一個事物。
  4. 使用者B 也進行查詢操作,此時處于等待中(阻塞狀态)。ps:需要等待使用者A事務送出後,才會執行。
注意:在事務中,隻有select…for update(排他鎖) 或lock in share mode(共享鎖) 操作同一個資料時才會等待其它事務結束後才執行,一般select... 則不受此影響。例如在 T3中執行select p.productCount from product p where p.productId = 1;則能正常查詢出資料,不會受第一個事務的影響。

2. 樂觀鎖(Optimistic Lock)

2.1 定義

樂觀鎖機制采取了更加寬松的加鎖機制。樂觀鎖是相對悲觀鎖而言,也是為了避免資料庫幻讀、業務處理時間過長等原因引起資料處理錯誤的一種機制,但樂觀鎖不會刻意使用資料庫本身的鎖機制,而是依據資料本身來保證資料的正确性。

實作樂觀鎖一般來說有以下2種方式:

  • 使用版本号

    使用資料版本(Version)記錄機制實作,這是樂觀鎖最常用的一種實作方式。何謂資料版本?即為資料增加一個版本辨別,一般是通過為資料庫表增加一個數字類型的 “version” 字段來實作。當讀取資料時,将version字段的值一同讀出,資料每更新一次,對此version值加一。當我們送出更新的時候,判斷資料庫表對應記錄的目前版本資訊與第一次取出來的version值進行比對,如果資料庫表目前版本号與第一次取出來的version值相等,則予以更新,否則認為是過期資料。

  • 使用時間戳

    樂觀鎖定的第二種實作方式和第一種差不多,同樣是在需要樂觀鎖控制的table中增加一個字段,名稱無所謂,字段類型使用時間戳(timestamp), 和上面的version類似,也是在更新送出的時候檢查目前資料庫中資料的時間戳和自己更新前取到的時間戳進行對比,如果一緻則OK,否則就是版本沖突。

2.2 案例分析

version

我們以

版本号

實作的方式進行說明。

查詢目前事務隔離級别:

SELECT @@tx_isolation;


結果:
REPEATABLE-READ

           

第一種測試

使用者A 使用者B
select * from product p where p.productId = 1;(productCount=100)
update product p set p.productCount = 99,version=version+1 where p.productId = 1 and version = 1;(受影響的行: 1)
T5
T6 update product p set p.productCount = 99,version=version+1 where p.productId = 1 and version = 1;(等待中...)
T7 commit;
T8 T6執行(受影響的行: 0)
T9
  1. 事務A開啟事務。
  2. 事務A查詢目前小米手機數量為100。
  3. 事務A購買小米手機,小米手機數量更新為99。(此時并未送出事務)。
  4. 事務B開啟事務。
  5. 事務B查詢目前小米手機數量為100。
  6. 事務B購買小米手機,小米手機數量更新為99。注意:此時處于阻塞狀态。
  7. 事務A送出事務。
  8. 此時第六步執行完畢,但并未成功(受影響的行: 0)。
  9. 事務B送出事務。

第二種測試

update product p set p.productCount = 99,version=version+1 where p.productId = 1 and version = 1;(受影響的行: 0)

樂觀鎖小結

  • 使用者B修改資料的時候,受影響行數為0,對業務來說,及更新失敗。這時候我們隻需要告訴使用者購買失敗,重新查詢一遍即可。
  • 對比第一種和第二種測試,我們會發現第一種測試,将update語句放入事務中會出現阻塞的情況,而第二種測試不會出現阻塞情況。這是為什麼呢?update其實在不在事務中都無所謂,在内部是這樣的:update是單線程的,及如果一個線程對一條資料進行update操作,會獲得鎖,其他線程如果要對同一條資料操作會阻塞,直到這個線程update成功後釋放鎖。
樂觀鎖不需要資料庫底層的支援!

3. 适用場景

悲觀鎖

比較适合寫入操作比較頻繁的場景,如果出現大量的讀取操作,每次讀取的時候都會進行加鎖,這樣會增加大量的鎖的開銷,降低了系統的吞吐量。

樂觀鎖

比較适合讀取操作比較頻繁的場景,如果出現大量的寫入操作,資料發生沖突的可能性就會增大,為了保證資料的一緻性,應用層需要不斷的重新擷取資料,這樣會增加大量的查詢操作,降低了系統的吞吐量。

文末

本章節主要簡單介紹了資料庫中

樂觀鎖與悲觀鎖

的相關知識,後續我們将會繼續介紹資料庫中的其他鎖以及相關知識。例如行鎖、表鎖、死鎖、

歡迎關注個人微信公衆号:Coder程式設計

擷取最新原創技術文章和免費學習資料,更有大量精品思維導圖、面試資料、PMP備考資料等你來領,友善你随時随地學習技術知識!

建立了一個qq群:315211365,歡迎大家進群交流一起學習。謝謝了!也可以介紹給身邊有需要的朋友。

文章收錄至

Github: https://github.com/CoderMerlin/coder-programming

Gitee: https://gitee.com/573059382/coder-programming

歡迎關注并star~

面試必備的資料庫悲觀鎖與樂觀鎖

參考文章:

https://chenzhou123520.iteye.com/blog/1860954

https://chenzhou123520.iteye.com/blog/1863407

推薦閱讀

帶你了解資料庫中JOIN的用法

帶你了解資料庫中事務的ACID特性

5分鐘帶你讀懂事務隔離性與隔離級别

歡迎關注公衆号:Coder程式設計