基於檔案鎖和redis的方案

2021-10-10 22:50:59 字數 2344 閱讀 4216

情景1:訂單a和訂單b同事進行操作,同時進行了庫存的操作,比如倉儲系統的發貨操作(減庫存),因為a和b包含的產品有重疊的部分,由於mysql是允許併發查詢的,所以a可能讀到b改之前的庫存數,在進行庫存修改造成資料錯誤,尤其是計算期初和結餘時(有人說用樂觀鎖加版本號的形式,但是乙個訂單操作往往是乙個事物中完成,需要操作很多sku的庫存最後才commit,如果因為乙個sku庫存更新失敗而回滾整個事物,和最早開始的文加鎖就沒區別了,還不如不操作更節省資源,所以比較好的方式是排它鎖),此時如果加入簡單檔案鎖可以保證a操作時b無法操作,避免資料錯誤,但是b很可能會等待超時失敗,加入訂單很多,重複很多,其中有訂單操作時間又長,鎖乙個庫的方式勢必會造成等待和大量訂單失敗。

所以乙個是將鎖顆粒變小,減小鎖的範圍(每個庫多個鎖),二是及時解鎖解除無效占用。

這裡鎖要滿足以下原則:

1.排它(互斥),a上鎖的部分b不可操作

2.不能死鎖,或者有解決死鎖的方法

3.誰上的鎖誰解,a不能解b的鎖

一、檔案鎖可以保證互斥

之前有個簡單的檔案鎖方案,這次由於操作的單量比之前大很多,又要求速度,上鎖就更精細了,不能只能到庫。

所以領導決定將每個庫分成多份,比如100份,也就是100個鎖,這樣可以提高精細度,不至於一次鎖乙個庫,其他人都操作不了。怎麼實現呢,方案就是用「庫id+sku_id%100"作為檔案名字,建立這個檔案就鎖住了所有sku在這個範圍內的,

但是這樣就會產生乙個問題,上鎖顆粒度是小了,但是多個訂單擁有的sku大概率會有有重疊的部分,比如

訂單1:sku:1   2  3  4 5  6  7  8  9

訂單2:sku:                 5  6  7   10  11  12

訂單3:sku: 5  6   7  15  19

如果訂單1把 5 鎖住的時候  訂單2把6鎖住了,訂單1再拿6時會失敗,訂單2再拿5時也會失敗,相互都拿不到對方的鎖,又擁有對方的鎖,造成死鎖(互卡),訂單3也是一樣的情況。為了解決死鎖問題,我決定使用redis

快取解決。

本來我想的是只用redis作上鎖,比如以"sku+庫id"做為key(也可以互斥),這樣可以讓顆粒度更小,直接類似於mysql的行鎖,但是老闆說只依賴redis他不放心,萬一redis掛了或者不好用了,他還是鍾情於之前的檔案鎖,遇到問題到時候直接刪檔案就好了,並且由於檔案就在專案伺服器中,避免了跨伺服器網路請求,會更快。我說其實redis可以做持久化和高可用的。但是為了讓老闆放(飯)心(碗),啥活也得接不是,那我們就做乙個使用檔案上鎖,用redis解鎖的方案。

大體思路就是:1、利用系統不允許同一路徑下 同時建立相同名稱的檔案(這個不用解釋吧,linux和windows都不行的),這就是鎖,假如我們有兩台linux伺服器,在a伺服器指定乙個目錄存放」鎖檔案",並將這個目錄和另乙個伺服器共享,將b伺服器同樣位置的目錄指向(對映,比如做nas)a的目錄,這樣就實現兩台伺服器乙個鎖,可以實現分布式鎖。2、利用redis做快取,將上鎖成功和失敗的資訊儲存進去,然後根據資訊做解鎖處理,再刪除資訊,這種反覆儲存redis快取的效率高而且是單執行緒(redis是序列處理命令的),可以省很多事(比如處理併發問題)。

那麼流程圖如下:

說明:中心思想就兩個方法,1:上鎖,2:解鎖。主要邏輯在於上鎖,上鎖時會將成功和失敗的訂單資訊和產品資訊存入redis,然後利用redis中的資訊進行判斷解鎖(解鎖策略)。

二、不能死鎖

從流程圖上看,不死鎖的方法有兩個,乙個是超時解鎖,乙個是及時解鎖。

超時解鎖不難理解,假如乙個訂單10個sku,當成功上了8個鎖,失敗的兩個還要迴圈去請求上鎖,也就是訂單還在處理中的狀態,但是不能無限等待下去,因為它自己也佔了8個鎖,所以會有超時時間 ,超過一定時間則會處理失敗,釋放掉它的鎖,允許其他訂單操作。這個策略不區分產生死鎖還是沒有得到鎖。

所謂及時解鎖其實是放棄加鎖,在產生死鎖時使用,因為要滿足第三個原則(誰上的鎖誰解),所以在加鎖的方法裡加入主動放棄的策略,即釋放自己鎖,本次加鎖失敗。但是如何判斷是否放棄還是繼續請求加鎖呢,就要基於redis中的資料進行權重計算,1.誰加鎖加的多誰有權利繼續加鎖,少的放棄,(加鎖比例)2.比例相同,誰先加鎖誰有權利繼續。

通過以上兩個策略避免了死鎖,當a訂單沒有和b產生死鎖,a會等待b處理完畢處理,若b時間過長,a會因超時放棄。多個訂單也是一樣道理。如果a和b在上鎖時發生互卡,他們自己必須判斷是否要放棄加鎖,最終會有一方放棄,即使判斷時有乙個執行緒壞了(比如重啟伺服器),另一方也會因超時解鎖。

那麼源**如下:

問題:一。加入檔案鎖,**稍顯複雜。邏輯設計也複雜了。

二。如果執行緒掛起或者崩潰,那麼會有檔案鎖殘留,需要手動刪除才能解除。

所以當時我是想直接用redis的,畢竟redis有設定超時時間的方式,超過時間可以自動失效,就不用考慮殘留問題。但這不是問題,將方法稍加改造就能實現了,需要注意的是redis的高可用和持久化問題。

基於redis的分布式互斥鎖方案

存乙個key到redis,設定失效時間,當程式需要獲取鎖的時候判斷這個key是否存在,如果存在就是其他執行緒正在使用鎖,將當前執行緒掛起,死迴圈獲取鎖。迴圈獲取鎖可以定義迴圈多長時間,每多久去獲取鎖優化一下。這種鎖是不公平的,但是相對來說效率也較好 實現jdk的lock介面,在lock 方法或者tr...

基於Cluster的Redis集群方案

說明 linux系統為centos6.4。要讓集群正常工作至少需要3個主節點,在這裡需要建立6個redis節點,其中三個為主節點,三個為從節點,對應的redis節點的ip和埠對應關係如下 127.0.0.1 7000 127.0.0.1 7001 127.0.0.1 7002 127.0.0.1 7...

redis分布式鎖的續鎖方案

基於redis的分布式鎖,很多人都有用過。今天簡單介紹基於redison客戶端的分布式鎖原始碼分析,重點內容是關於續鎖的問題,具體內容如下。1 基於redison客戶端建立分布式鎖工具,具體 如下圖 從圖1可以看出,這個工具類裡面主要的內容是初始化redisson客戶端,嘗試加鎖,釋放鎖三個操作。2...