Mysql乐观锁与悲观锁的区别
| 原文地址: 05 mysql-樂觀鎖與悲觀鎖的區別 |
文章目錄
- 1、悲觀鎖
- 2、樂觀鎖
- 3、兩種鎖的使用場景
- 4、樂觀鎖常見的兩種實現方式
- 4.1、 版本號機制
- 4.2、 CAS算法
- 5、樂觀鎖的缺點
- 5.1、 ABA 問題
- 5.2、 循環時間長開銷大
- 5.3、 只能保證一個共享變量的原子操作
- 6、悲觀鎖缺點
1、悲觀鎖
? ??總是假設最壞的情況,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞,用完后再把資源轉讓給其它線程)。傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。Java中synchronized和ReentrantLock等獨占鎖就是悲觀鎖思想的實現。總的來說,悲觀鎖是基于一種悲觀的態度類來防止一切數據沖突,它是以一種預防的姿態在修改數據之前把數據鎖住,然后再對數據進行讀寫,在它釋放鎖之前任何人都不能對其數據進行操作,直到前面一個人把鎖釋放后下一個人數據加鎖才可對數據進行加鎖,然后才可以對數據進行操作,一般數據庫本身鎖的機制都是基于悲觀鎖的機制實現的。
特點:可以完全保證數據的獨占性和正確性,因為每次請求都會先對數據進行加鎖, 然后進行數據操作,最后再解鎖,而加鎖和解鎖的過程會造成消耗,所以性能不高;
2、樂觀鎖
? ???總是假設最好的情況,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號機制和CAS算法實現。樂觀鎖適用于多讀的應用類型,這樣可以提高吞吐量,像數據庫提供的類似于write_condition機制,其實都是提供的樂觀鎖。在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現方式CAS實現的。總的來說,樂觀鎖對于數據沖突保持一種樂觀態度,操作數據時不會對操作的數據進行加鎖(這使得多個任務可以并行的對數據進行操作),只有到數據提交的時候才通過一種機制來驗證數據是否存在沖突(一般實現方式是通過加版本號然后進行版本號的對比方式實現)。
特點:樂觀鎖是一種并發類型的鎖,其本身不對數據進行加鎖通而是通過業務實現鎖的功能,不對數據進行加鎖就意味著允許多個請求同時訪問數據,同時也省掉了對數據加鎖和解鎖的過程,這種方式因為節省了悲觀鎖加鎖的操作,所以可以一定程度的的提高操作的性能,不過在并發非常高的情況下,會導致大量的請求沖突,沖突導致大部分操作無功而返而浪費資源,所以在高并發的場景下,樂觀鎖的性能卻反而不如悲觀鎖。
3、兩種鎖的使用場景
?? ??從上面對兩種鎖的介紹,我們知道兩種鎖各有優缺點,不可認為一種好于另一種,像樂觀鎖適用于寫比較少的情況下(多讀場景),即沖突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果是多寫的情況,一般會經常產生沖突,這就會導致上層應用會不斷的進行retry,這樣反倒是降低了性能,所以一般多寫的場景下用悲觀鎖就比較合適。
4、樂觀鎖常見的兩種實現方式
樂觀鎖一般會使用版本號機制或CAS算法實現。4.1、 版本號機制
?? ??一般是在數據表中加上一個數據版本號version字段,表示數據被修改的次數,當數據被修改時,version值會加一。當線程A要更新數據值時,在讀取數據的同時也會讀取version值,在提交更新時,若剛才讀取到的version值為當前數據庫中的version值相等時才更新,否則重試更新操作,直到更新成功。
舉一個簡單的例子: 假設數據庫中帳戶信息表中有一個 version 字段,當前值為 1 ;而當前帳戶余額字段( balance )為 $100 。
??操作員 A 此時將其讀出( version=1 ),并從其帳戶余額中扣除 50(100-$50 )。
在操作員 A 操作的過程中,操作員B 也讀入此用戶信息( version=1 ),并從其帳戶余額中扣除 20(100-$20 )。
??操作員 A 完成了修改工作,將數據版本號加一( version=2 ),連同帳戶扣除后余額( balance=$50 ),提交至數據庫更新,此時由于提交數據版本大于數據庫記錄當前版本,數據被更新,數據庫記錄 version 更新為 2 。
??操作員 B 完成了操作,也將版本號加一( version=2 )試圖向數據庫提交數據( balance=$80 ),但此時比對數據庫記錄版本時發現,操作員 B 提交的數據版本號為 2 ,數據庫記錄當前版本也為 2 ,不滿足 “ 提交版本必須大于記錄當前版本才能執行更新 “ 的樂觀鎖策略,因此,操作員 B 的提交被駁回。
這樣,就避免了操作員 B 用基于 version=1 的舊數據修改的結果覆蓋操作員A 的操作結果的可能。
4.2、 CAS算法
? ???即compare and swap(比較與交換),是一種有名的無鎖算法。無鎖編程,即不使用鎖的情況下實現多線程之間的變量同步,也就是在沒有線程被阻塞的情況下實現變量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三個操作數:需要讀寫的內存值 V、進行比較的值 A、擬寫入的新值 B
??當且僅當 V 的值等于 A時,CAS通過原子方式用新值B來更新V的值,否則不會執行任何操作(比較和替換是一個原子操作)。一般情況下是一個自旋操作,即不斷的重試。
5、樂觀鎖的缺點
ABA 問題是樂觀鎖一個常見的問題5.1、 ABA 問題
? ???如果一個變量V初次讀取的時候是A值,并且在準備賦值的時候檢查到它仍然是A值,那我們就能說明它的值沒有被其他線程修改過了嗎?很明顯是不能的,因為在這段時間它的值可能被改為其他值,然后又改回A,那CAS操作就會誤認為它從來沒有被修改過。這個問題被稱為CAS操作的 "ABA"問題。
?? ??JDK 1.5 以后的 AtomicStampedReference 類就提供了此種能力,其中的 compareAndSet 方法就是首先檢查當前引用是否等于預期引用,并且當前標志是否等于預期標志,如果全部相等,則以原子方式將該引用和該標志的值設置為給定的更新值。
5.2、 循環時間長開銷大
?? ??自旋CAS(也就是不成功就一直循環執行直到成功)如果長時間不成功,會給CPU帶來非常大的執行開銷。 如果JVM能支持處理器提供的pause指令那么效率會有一定的提升,pause指令有兩個作用,第一它可以延遲流水線執行指令(de-pipeline),使CPU不會消耗過多的執行資源,延遲的時間取決于具體實現的版本,在一些處理器上延遲時間是零。第二它可以避免在退出循環的時候因內存順序沖突(memory order violation)而引起CPU流水線被清空(CPU pipeline flush),從而提高CPU的執行效率。
5.3、 只能保證一個共享變量的原子操作
? ???CAS 只對單個共享變量有效,當操作涉及跨多個共享變量時 CAS 無效。但是從 JDK 1.5開始,提供了AtomicReference類來保證引用對象之間的原子性,你可以把多個變量放在一個對象里來進行 CAS 操作.所以我們可以使用鎖或者利用AtomicReference類把多個共享變量合并成一個共享變量來操作。
6、悲觀鎖缺點
? ???悲觀鎖雖然更能保證線程的安全性,但他并不是適用于所有的場景,它也有存在的一些不足,因為悲觀鎖大多數情況下依靠數據庫的鎖機制實現,以保證操作最大程度的獨占性。如果加鎖的時間過長,其他用戶長時間無法訪問,影響了程序的并發訪問性,同時這樣對數據庫性能開銷影響也很大,特別是對長事務而言,這樣的開銷往往無法承受。
總結
以上是生活随笔為你收集整理的Mysql乐观锁与悲观锁的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 批量下载依赖_python
- 下一篇: 秒杀系统相关