Java面試題 Part11 樂觀鎖與悲觀鎖

編程語言 Java Redis NoSQL Java個人學習心得 Java個人學習心得 2017-09-07

Java面試題 Part11 樂觀鎖與悲觀鎖

Redis

面試的時候遇到過問Redis是如何解決“競態條件”的,相關知識點總結一下。

樂觀鎖

所謂競態條件,舉個例子,一個代表點擊數的數值hitcount,每個客戶點擊一次則+1。

沒有事務的時候,假設我們的操作如下:

hc=GET hitcount;

hc=hc+1;

SET hitcount $hc;

非併發狀態下,這樣做是OK的,但是併發狀態下會出現的問題是:

Java面試題 Part11 樂觀鎖與悲觀鎖

1

A和B兩個客戶端分別從Redis處取值,並+1,值都是11。

Java面試題 Part11 樂觀鎖與悲觀鎖

2

Redis是單線程模型,所以A和B的SET命令只能先執行1個,此處先執行A,hitcount更新為11。

Java面試題 Part11 樂觀鎖與悲觀鎖

3

接著執行B的SET命令,hitcount依然是11,這就是明顯的因為競態而產生的錯誤,hitcount應該為12才是。

Redis的事務其實是通過MULTI命令開啟事務,將後續一系列的命令放在一個隊列裡,不立即執行,直到EXEC命令,隊列中的命令才會依次執行。

命令類似:

MULTI;

set val1 111;

set val2 222;

EXEC;

在實際工作中,我們也會經常遇到這種問題:我們必須先拿到數據,根據數據做出判斷,進行一些處理之後,再更新數據,這時候我們就沒法保證取數據、更新數據在同一個隊列事務中了。

這就需要樂觀鎖。簡單說,我們每取一個數據的時候,Redis不僅返回數值,還會返回這個數值的版本號。

當我們執行更新命令時,Redis會拿你要SET值的版本號與庫裡現在值的版本號進行比對,如果相同,則更新,版本號變更。

如果版本號不同,則說明在我們執行更新命令之前,有其他客戶端修改了這條數據,我們的更新操作失敗。

Redis裡是通過WATCH命令來監控版本號的。

WATCH hitcount;

hc=GET hitcount;

hc=hc+1;

MULTI;

SET hitcount $hc;

EXEC;

因為通過WATCH監控了hitcount這個Key,那麼在事務中SET的時候,一旦發現版本號不對,執行就失敗。

悲觀鎖

樂觀鎖是CAS——Check And Set,先檢查(版本號)再設置更新,那麼悲觀鎖就是先鎖,再查,最後更新。

以MySQL為例,我們要實現悲觀鎖:

SELECT * FROM tabA AS t WHERE t.id=1 FOR UPDATE;

這樣,因為主鍵是明確指定的(id=1),所以這一行記錄就被鎖定了——行鎖定,在本事務被commit之前,這條數據都只會被本事務的SQL語句進行修改,其他事務的加鎖操作,更新操作都會在本事務提交之後再執行,單純的查詢則不受影響。

如果沒有指定明確地主鍵,則鎖定的是表——表鎖定

相關推薦

推薦中...