常見的鎖(樂觀鎖和悲觀鎖)

鎖概念

    當多個用戶併發地存取數據時,在數據庫中就會產生多個事務同時存取統一數據的情況。若對併發操作不加控制就可能會讀取和存儲不正確的數據,破壞數據庫的一致性。所以,鎖主要用於處理併發問題。

    從數據庫系統角度分爲三種:排他鎖、共享鎖、更新鎖。

    從程序員角度分爲兩種:一種是悲觀鎖、一種樂觀鎖。

 

悲觀鎖

    總是假設最壞的情況,每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖。
    傳統的關係數據庫裏用到了很多這種鎖機制,比如按使用性質劃分的讀鎖、寫鎖和按作用範圍劃分的行鎖、表鎖。
 
對於可能衝突的併發操作,以串行的方式取代併發執行,因而它也是一種悲觀併發控制。
 

1、共享鎖(事務只能讀)

共享鎖(Shared lock)(S鎖)又稱爲讀鎖,若事務T對像A加上S鎖,則事務T只能讀A;其他事務只能對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。。這就保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改
 
 
 

2、排他鎖

排他鎖(X鎖)又稱爲寫鎖,若事務T對數據對象A加上X鎖,則指允許T讀取和修改A,其他任何事務都不能再對A加任何類型的鎖,直到T釋放A上的鎖。這就保證了其他事務在T釋放A上的鎖之前不能再讀取和修改A。
 

3、表鎖

每次操作鎖住整張表,開銷小,加鎖快,鎖粒度大,發生鎖衝突的概率高,併發度最小
 


4、行鎖

每次操作鎖住一行數據,開銷大,加鎖慢,鎖粒度小,發生鎖衝突的概率最低,併發度最高。
數據庫能夠確定那些行需要鎖的情況下使用行鎖,如果不知道會影響那行的時候會使用表鎖。
 

樂觀鎖(沒有用鎖,使用了一些比較,如果比較符合自己的想法,就交換或執行)

總是假設假設最好的情況,每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但是在更新的時候會判斷以下在此期間別人有沒有去更新這個數據,可以使用版本號機制和CAS算法實現
 
允許可能衝突的操作併發執行 是一種樂觀的併發策略
 

1、版本號機制

一般是在數據表中加入一個數據版本號version字段,表示數據被修改的次數,當數據被修改時,version指會加一。當線程A要重新更新數據值時,在讀取數據的時候也會讀取version值,在提交更新時,若剛纔讀取到的version值與當前數據庫中的version值相等才更新,否則重新更新操作,直到更新成功。
 

2、CAS算法

CAS 即compare and swap(比較並交換),是一種有名的無鎖算法,在不適用鎖的情況下實現多線程之間的變量同步,也就是在沒有線程被阻塞的情況下實現變量同步,所以也叫非阻塞同步。CAS算法涉及到三個操作數:
1、要更新的變量V
2、預期的值E
3、新值N
僅當V值等於E值時,纔會將V的值設置爲N,否則說明都不做。最後CAS返回當前V的值。CAS算法需要你額外給出一個期望值,也就是你認爲現在變量應該是是樣子的,如果變量不是你想象的樣子,就說明已經被別人修改過,就重新讀取,再次嘗試修改即可。
 
因爲CAS需要在操作值得時候檢查下值有沒有發生變化,如果沒有發生變化則更新,但如果一個值原來是A,變成了B,由變成了A,那麼使用CAS進行檢查時就會誤以爲它的值沒有發生變化,這個問題稱爲ABA問題。ABA問題的解決思路就是使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號加一,那麼A-B-A就會變成1A-2B-3A,以此來防止不恰當的寫入。
 

兩種鎖得適用場景

樂觀鎖適用於寫比較少(多讀場景),即衝突真得很少發生得時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果是多寫的情況,一般會經常產生衝突,這就會導致上層應用會不斷的進行重試,這樣反倒是降低了性能,所以一般多寫的場景下用悲觀鎖比較合適
 
疑問?
數據庫中得事務 實現使用悲觀鎖還是樂觀鎖?還是兩個都用
結果是 兩個都用,不同的場景用不同的鎖!
一個線程中的事務獲取到了數據庫中的數據 加了樂觀鎖或悲觀鎖 
如果寫較小少用樂觀鎖,寫較多用悲觀鎖。
然後另一個線程的事務也涉及到這個數據庫中的這個數據,但是被加了鎖,所以只能等這個事務執行完了釋放了 才能執行。
事務的本質就是一個函數方法,裏面有多個sql執行語句。只不過這個方法是(隔離性)獨立執行的,原子性的(一定執行完的 不然回滾)
所以事務中的多個sql執行語句能夠串行執行下來。
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章