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

2021-09-27 09:03:50 字數 4517 閱讀 7649

在上乙個章節5分鐘帶你讀懂事務隔離性與隔離級別的最後,其實我們已經提到了鎖的概念。本章節接下來將主要介紹以下資料庫悲觀鎖與樂觀鎖的相關知識。如有錯誤還請大家及時指出~

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

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

其他知識點

悲觀鎖主要是共享鎖排他鎖

共享鎖又稱為讀鎖,簡稱s鎖,顧名思義,共享鎖就是多個事務對於同一資料可以共享一把鎖,都能訪問到資料,但是只能讀不能修改。

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

使用場景舉例:以mysql innodb為例

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

productid

productname

productprice

productcount1小公尺

1999

1002

魅族1999

100 首先我們需要set autocommit=0,即不允許自動提交

問題在併發情況下回導致資料一致性的問題:

如果有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

事務bt1

start transaction;

t2select p.productcount from product p where p.productid = 1 for update;

t3start transaction;

t4select p.productcount from product p where p.productid = 1 for update;(等待中...)

流程說明

使用者a start transaction開啟乙個事物。前一步我們關閉了mysql的autocommit,所以需要手動控制事務的提交。

在獲得小公尺手機資訊(productid = 1 )時,進行資料加鎖操作(for update)。與普通查詢方式不同,我們使用了select…for update的方式,這樣就通過資料庫實現了悲觀鎖。在這個update事務提交之前其他外界是不能修改這條資料的,但是這種處理方式效率比較低,一般不推薦使用。

使用者b start transaction開啟乙個事物。

使用者b 也進行查詢操作,此時處於等待中(阻塞狀態)。ps:需要等待使用者a事務提交後,才會執行。

注意:在事務中,只有select…for update(排他鎖) 或lock in share mode(共享鎖) 操作同乙個資料時才會等待其它事務結束後才執行,一般select... 則不受此影響。例如在 t3中執行select p.productcount from product p where p.productid = 1;則能正常查詢出資料,不會受第乙個事務的影響。

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

其他知識點

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

使用場景舉例:以mysql innodb為例

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

productid

productname

productprice

productcount

version1小公尺

1999

10012魅族

1999

1002

我們以版本號實現的方式進行說明。

操作

查詢當前事務隔離級別:

select @@tx_isolation;

結果:repeatable-read

下面同時開啟兩個視窗模擬2個使用者併發訪問資料庫

第一種測試

時間軸使用者a使用者b

t1start transaction;

t2select * from product p where p.productid = 1;(productcount=100)

t3update product p set p.productcount = 99,version=version+1 where p.productid = 1 and version = 1;(受影響的行: 1)

t4start transaction;

t5select * from product p where p.productid = 1;(productcount=100)

t6update product p set p.productcount = 99,version=version+1 where p.productid = 1 and version = 1;(等待中...)

t7commit;

t8t6執行(受影響的行: 0)

t9commit;

流程說明

事務a開啟事務。

事務a查詢當前小公尺手機數量為100。

事務a購買小公尺手機,小公尺手機數量更新為99。(此時並未提交事務)。

事務b開啟事務。

事務b查詢當前小公尺手機數量為100。

事務b購買小公尺手機,小公尺手機數量更新為99。注意:此時處於阻塞狀態。

事務a提交事務。

此時第六步執行完畢,但並未成功(受影響的行: 0)。

事務b提交事務。

第二種測試

時間軸使用者a使用者b

t1select * from product p where p.productid = 1;(productcount=100)

t2update product p set p.productcount = 99,version=version+1 where p.productid = 1 and version = 1;(受影響的行: 1)

t3select * from product p where p.productid = 1;(productcount=100)

t4update product p set p.productcount = 99,version=version+1 where p.productid = 1 and version = 1;(受影響的行: 0)

樂觀鎖小結

樂觀鎖不需要資料庫底層的支援!

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

樂觀鎖

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

本章節主要簡單介紹了資料庫中樂觀鎖與悲觀鎖的相關知識,後續我們將會繼續介紹資料庫中的其他鎖以及相關知識。例如行鎖、表鎖、死鎖、

悲觀鎖 面試必備之 樂觀鎖與悲觀鎖

一 什麼是悲觀鎖?什麼是樂觀鎖?1 鎖 lock 在介紹樂觀鎖和悲觀鎖之前,我們先介紹下鎖。在日常生活中,我們經常接觸到鎖這個東西,比如家裡門上的鎖,自行車上的鎖,保險櫃上的鎖等,這些都是為了保障我們的財產安全而上的鎖。而在程式中,鎖是一種保障資料安全的機制和手段。在多併發的情況,當同時多個請求修改...

資料庫樂觀鎖與悲觀鎖

每次拿資料的時候都會擔心會被別人修改 疑心重很悲觀 所以每次在拿資料的時候都會上鎖。確保自己使用的過程中不會被別人訪問,自己使用完後再解鎖。期間需要訪問該資料的都會等待。每次拿資料的時候都完全不擔心會被別人修改 心態好很樂觀 所以每次在拿資料的時候都不會上鎖。但是在更新資料的時候去判斷該期間是否被別...

資料庫樂觀鎖與悲觀鎖

演示案例 為何需要樂觀鎖,與悲觀鎖這樣的鎖?idname money 1god 1000 假設god同志的賬上有1000元,現在有兩個執行緒同時往他的賬戶上轉錢。1.a執行緒準備向god賬戶上轉200,讀取到賬戶上有1000元,事務還未提交 2.b執行緒準備向god賬戶上轉100,讀取到賬戶上有10...