动态

详情 返回 返回

聊聊併發控制鎖 - 动态 详情

對於企業應用來説,完全不涉及到併發的問題,基本是不可能的。因為對於一個應用中很多的事情都是同時進行的。併發可能發生在數據獲取,服務調用乃至於用户交互中。併發問題有兩個重要的解決方案,一個是隔離,另一個是不變性。

併發問題會發生在多個執行單元同時訪問同一資源的時候,此時,一個好的方法就是分好“蛋糕”,讓每一個執行單元都能訪問到各自的資源。好的併發設計就是:找到創建好隔離區的辦法,然後通過分析工作流讓隔離區能夠完成儘可能多的任務。

在共享數據可以改變的情況下,併發問題就有可能發生。從實際的場景出發,同時有兩個客户詢問兩位服務員是否還有某一貨品時,兩位服務員各自去查看了一下系統並回復客户還有一份,兩位客户中一定有一位會失望。那麼這件事情的解決方案就是添加隔離區(購物車),服務員把當前貨品放入客户的購物車成功後告知用户,然後失敗的一方就可以告知用户貨品已經銷售一空。雖然存在已購用户退貨的可能,但無疑比前一結果要好太多。這也就是下文中所説的悲觀鎖。

下面我們開始介紹兩種併發控制策略:

樂觀和悲觀併發控制

在某個系統中,同時有兩個企業員工 A 和 B 想要編輯同一個用户信息。此時 A 和 B 都獲取到了用户的信息數據。然後他們兩個進行了修改,A 員工先完成了操作並且進行了提交。然後 B 員工完成了操作也進行了提交。此時系統中的這個用户信息只保留了 B 提供的數據,而丟棄了 A 員工的數據。這可能會造成一些難以預料的問題,甚至有可能導致他們丟掉工作。雖然可以通過操作日誌來追溯到是哪個員工操作了數據,但這個信息沒有任何意義,因為系統並沒有讓任何員工得知修改這一情況。

當一些可變數據無法隔離時候,我們可以用兩種不同的控制策略:樂觀鎖策略和悲觀鎖策略。樂觀鎖用於衝突檢測,悲觀鎖用於衝突避免。

悲觀者策略非常簡單,當 A 用户獲取到用户信息時系統把當前用户信息給鎖定,然後 B 用户在獲取用户信息時就會被告知別人正在編輯。等到 A 員工進行了提交,系統才允許 B 員工獲取數據。此時 B 獲取的是 A 更新後的數據。

樂觀者策略則不對獲取進行任何限制,這時候我們可以在用户信息中添加版本號來告知用户信息已被修改。樂觀鎖要求每條數據都有一個版本號,同時在更新數據時候就會更新版本號,如 A 員工在更新用户信息時候提交了當前的版本號。系統判斷 A 提交的時候的版本號和該條信息版本號一致,允許更新。然後系統就會把版本號修改掉,B 員工來進行提交時攜帶的是之前版本號,此時系統判定失敗,要求 B 重新獲取數據和版本號,然後再一次進行提交。

樂觀鎖和悲觀鎖進行選擇的標準是: 衝突的頻率和嚴重性。如果衝突的結果對於用户是難以接受的,我們只能採用悲觀鎖策略。如果衝突的結果不會很嚴重,或者頻率也較低,我們就可以選擇樂觀鎖,它更容易實現,也具有更好的併發性。

當然,我們也可以對樂觀鎖進行一些優化,把更新時間(作為版本號)和更新用户添加到信息中,如此以來,系統就可以告知 B 員工該條信息被修改過,以及在何時何人操作。系統還可以提供給 B 新的更新時間以及是否強制更新的選擇。當然,甚至可以基於業務需求以及日誌信息等來告知 B 員工之前具體修改的信息。

死鎖

使用悲觀鎖技術有一個特別的問題就是死鎖,即用户在已經獲取鎖的情況下還想要獲取更多的鎖。以最早的兩個客户的問題來説,就是水果蛋糕需要獲取水果和蛋糕,兩個用户各有其中一種,並期望獲取對方東西。

解決死鎖的方法是檢測處理和超時控制。

檢查處理會檢測出死鎖發生並且會選擇一個“犧牲者”,讓他放棄他所擁有的已保證另外一個客户可以獲取水果蛋糕。而超時控制則是給每個鎖添加一個超時時間,一旦達到了超時時間,當前的購物車裏面的物品就被清掉。

超時控制和檢測機制用於已經發生了死鎖的情況,而另外的方法則是避免死鎖的發生。防止死鎖的方法就是在用户獲取鎖的時候就獲取所有可能需要的鎖,粗力度鎖(這很保守,但很有效),即水果蛋糕不是由兩個貨品組合而成的。

粗力度鎖是覆蓋多個資源的單個鎖,這樣會簡化多個鎖帶來的複雜性。這其實也會發生在樂觀鎖的過程中,例如用户和用户相關地址信息,如果用户地址信息修改後也會更改用户信息,這樣如何獲取和設置樂觀鎖呢?我們需要尋找到一組資源的核心。

同時,找到一組資源的核心也會使得開發的代碼邏輯更加清晰。大家不妨想一下,在數據庫層面的操作中,是選擇先更新子表然後再去更新主表這樣的邏輯順序更好,還是以主表為入口進行更新修改更好呢?

鼓勵一下

如果你覺得這篇文章不錯,希望可以給與我一些鼓勵,在我的 github 博客下幫忙 star 一下。

博客地址

user avatar hard_heart_603dd717240e2 头像 u_17443142 头像 ecomools 头像 hankin_liu 头像 motianlun_5d0766992e67a 头像 user_ze46ouik 头像 robin_ren 头像 yqyx36 头像 79px 头像 baozouai 头像 wqjiao 头像 gaoxingdeqincai 头像
点赞 32 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.