當前位置:
首頁 > 科技 > 一個悲觀鎖和樂觀鎖的小白故事

一個悲觀鎖和樂觀鎖的小白故事

在大型分散式系統中,不可避免的會遇到大量並發寫入的場景。在這種場景下如何進行更好的並發控制,即在多個任務同時存取數據時保證數據的一致性,成為分散式系統必須解決的問題。

悲觀並發控制和樂觀並發控制是並發控制中採用的主要技術手段,對於不同的業務場景,應該選擇不同的控制方法也讓開發者頭疼,今天讓通過一個故事(來源:荒唐的程序猿),講解下其原理和優劣勢。

搬好板凳,故事馬上開始了。

旺財和小強生活在一個網上商城的系統中, 是一對兒線程好基友。

星期一剛上班,旺財接到領導電話說,要把一個商品的庫存減少20, 旺財不敢怠慢,趕快把庫存取出來一看,哦,現在有1000個。

與此同時,小強也接到電話說要把同一商品的庫存減少30,他一看,哦,現在有1000個。

旺財計算出最新的庫存值980,保存!

小強也計算出最新的庫存值970,保存 !

旺財的數據被小強覆蓋了!

領導一看,本來賣出了50個商品,現在庫存只扣了30個,這樣持續下去就天下大亂了。

旺財和小強,各打二十大板,長長記性!

小強說:「哥,要不我們還是想個辦法吧,再這樣下去要被打死的。」

旺財悲催地說: 「這樣, 以後我們每次訪問庫存之前,都要先加鎖,加了鎖,就禁止別人再進入訪問,只能等待持有鎖的人來釋放。」

星期二, 領導讓旺財再次把庫存減少20 , 旺財這次萬分小心,先把庫存給鎖住,然後慢慢修改。

小強也接到了把庫存減少的指令, 但是旺財哥已經把庫存鎖住了, 不能操作,小強只好去阻塞車間喝茶聊天,然後到就緒車間等待調度運行。

好不容易等到可以再次執行了,小強一看,這庫存怎麼還鎖著呢!?只好再次去阻塞車間喝茶。

領導一看,小強你怎麼回事,老是喝茶聊天?還干不幹活了?

小強爭辯說旺財哥一直鎖著庫存,我沒法操作。

領導不管這些,把小強和旺財又打了二十大板。

備註:這種加鎖的方式就是悲觀鎖了,悲觀鎖正如其名,每次取讀寫數據時候總認為數據會被別人修改,所以將數據加鎖,置於鎖定狀態, 不讓別人再訪問。缺點是如果持有鎖的時間太長,其他用戶需要等待很長時間。

旺財說: 「兄弟,這一次哥對不住你啊,處理得慢了一些,不過哥剛才挨打的時候想了一個好辦法:樂觀鎖。」

小強說:「拉倒吧你,屁股都快被打爛了還樂觀?」

「你聽我說嘛, 我們在那個庫存欄位的旁邊,再加上一個版本(version)的欄位, 例如剛開始的時候(庫存= 1000, 版本=1), 每次你去讀的時候不僅要讀出庫存,還要讀出版本號, 等到你修改了庫存,往回寫的時候一定要檢查一下版本號,看看和你讀的時候是否一樣。」

「如果不一樣呢?」 小強問

「那就放棄這次寫的操作,重新讀取庫存和版本號, 重新來過。」

「如果一樣呢? 」

「那就放心大膽地把新的庫存值寫回去。把版本號也加1」

「我似乎有點明白了,我們試試,不過你要想好,我可不想再挨板子了。」

星期三, 旺財奉命把庫存減去30, 他先讀到了(庫存= 1000, 版本=1), 小強也要改庫存了,他要把庫存減去50,於是他也讀到了(庫存= 1000, 版本=1)。

旺財計算出新的庫存值970 ,寫回成功,現在版本變成了(庫存= 970, 版本=2)。

小強也計算出新庫存950 ,也準備寫回,他戰戰兢兢地去看最新的版本號,已經變成版本2了, 按照之前的約定,只好重新讀取了。

小強再次讀取(庫存= 970,版本=2),計算出最新緩存值920(970減去50),再次試圖更新,沒想到又被別人搶了先,現在版本號已經變成3了 ,最新的數據是(庫存= 900,版本=3)。

唉, 只好重新來過, 計算出最新緩存值850(900減去50),第三次終於更新成功了, 最新的庫存是 (庫存=850, 版本= 4)

領導看到旺財和小強忙得熱火朝天,一刻不停,並且庫存值也沒有亂掉, 滿意地點了點頭:好同志啊。

備註:這種方式就是所謂的樂觀鎖了,旺財和小強這次樂觀了一點,覺得一般情況下不會有太多人修改庫存,所以沒有加鎖,放心地去操作,只有在最後更新的時候才去看是否衝突。 這種方式適合於衝突不多的場景,如果衝突很多,數據爭用激烈,會導致不斷地嘗試,反而降低了性能。

故事完

兩種鎖機制總結:

悲觀鎖(全稱Pessimistic Concurrency Control,縮寫PCC)是一種並發控制的方法。它可以阻止一個事務以影響其他用戶的方式來修改數據。如果一個事務執行的操作讀某行數據應用了鎖,那只有當這個事務把鎖釋放,其他事務才能夠執行與該鎖衝突的操作。

在悲觀鎖的場景下,假設用戶A和B要修改同一個文件,A在鎖定文件並且修改的過程中,B是無法修改這個文件的,只有等到A修改完成,並且釋放鎖以後,B才可以獲取鎖,然後修改文件。

由此可以看出,悲觀鎖對並發的控制持悲觀態度,它在進行任何修改前,首先會為其加鎖,確保整個修改過程中不會出現衝突,從而有效的保證數據一致性。但這樣的機制同時降低了系統的並發性,尤其是兩個同時修改的對象本身不存在衝突的情況。同時也可能在競爭鎖的時候出現死鎖,所以現在很多的系統例如Kubernetes採用了樂觀並發的控制方法。

樂觀鎖(全稱Optimistic Concurrency Control,縮寫OCC)假設多用戶並發的事務在處理時不會彼此影響,各事務能夠在不請求鎖的情況下處理各自的數據。在提交數據更新之前,每個事務會先檢查在該事務讀取數據後,有沒有其他事務又修改了該數據。如果其他事務有更新的話,正在提交的事務會進行回滾。

相對於悲觀鎖對鎖的提前控制,樂觀鎖相信請求之間出現衝突的概率是比較小的,在讀取及更改的過程中都是不加鎖的,只有在最後提交更新時才會檢測衝突,因此在高並發量的系統中佔有絕對優勢。同樣假設用戶A和B要修改同一個文件,A和B會先將文件獲取到本地,然後進行修改。如果A已經修改好並且將數據提交,此時B再提交,伺服器端會告知B文件已經被修改,返回衝突錯誤。此時衝突必須由B來解決,可以將文件重新獲取回來,再一次修改後提交。

樂觀鎖通常通過增加一個資源版本欄位,來判斷請求是否衝突。初始化時指定一個版本值,每次讀取數據時將版本號一同讀出,每次更新數據,同時也對版本號進行更新。當伺服器端收到數據時,將數據中的版本號與伺服器端的做對比,如果不一致,則說明數據已經被修改,返回衝突錯誤。

本號涉及技術和總結(20+)

可識別小程序獲取電子書詳細信息

溫馨提示:

求知若渴, 虛心若愚


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 架構師技術聯盟 的精彩文章:

Gartner 2018新技術成熟度曲線
詳談Lustre背後的故事,ZFS前世和今生

TAG:架構師技術聯盟 |