使用redis構建可靠分布式鎖

2022-08-01 12:21:12 字數 2757 閱讀 8022

關於分布式鎖的概念,具體實現方式,直接參閱下面兩個帖子,這裡就不多介紹了。

分布式鎖的多種實現方式

分布式鎖總結

對於分布式鎖的幾種實現方式的優劣,這裡再列舉下

1. 資料庫實現方式

優點:易理解

缺點:運算元據庫消耗較大,效能較低。為了處理一些異常,會使得整個方案越來越複雜

2. 快取實現方式

優點:效能好,實現起來較為方便。

缺點:通過超時時間來控制鎖的失效時間並不是十分的靠譜。

3 zookeeper實現

優點:有效的解決單點問題,不可重入問題,非阻塞問題以及鎖無法釋放的問題。

缺點:效能上不如使用快取實現分布式鎖

第二篇帖子中,談到redis實現分布式鎖時,提了一些建議

"redis如果能像zookeeper一樣,實現了和客戶端繫結的臨時key,一旦redis客戶端掛了,臨時key刪除,通知watchkey的其他客戶端(感覺這個是乙個不錯的需求,不知redis未來是否要實現),就可以消除鎖超時,再使用redlock實現的分布式鎖,這時候可靠性就更高了。"

就效能而言,redis比zookeeper具有天然優勢,而它的缺點也可以通過一些機制來另外改進。所以就嘗試著修改了redis的原始碼,看能否解決上述問題。

修改點一:增加一條命令settp

settp(tp 可以理解為temporary的縮寫),故名思議,就是乙個臨時的key。

命令格式:settp key value

首先使用這條命令,必須保證key是不存在的,即這個命令具有setnx命令的屬性,然後在新增完key之後,將這個key加入到執行這條命令client的乙個list裡面。這個list專門用來儲存臨時鍵。那麼在redis客戶端掛了,或者意外斷開連線時,在呼叫freeclient()函式時,便可以將臨時鍵清理掉。就不會影響其他client再次獲取鎖

修改點二:增加命令watchex

命令格式:watchex key

返回:redisreply是乙個字串型別

如果key存在,則str內容為"exist"

如果key不存在,則str內容為"noexist"

如果key被新增,返回"add";key被刪除時,返回"del"

示例如下:

首先執行hiredis-example-ae,對應的原始檔是example-ae.c

在另乙個視窗中執行如下命令

可以看到在刪除或者新增某個key時,在第乙個視窗中都會收到通知

如果不想再watchex某個key,執行unwatchex key命令即可。

這個命令的實現原理其實有點類似redis 自身的pubsub機制,但是pubsub有乙個侷限就是,執行了該命令之後,就不能執行其他命令,只能等待channel上的資訊。這種方式顯然不適用於我們的場景。

我們的實現方式是,首先需要在client中儲存乙個所有watchex的list,然後在系統增加乙個dict,用於儲存每個被watchex的key。這個dict的鍵就是被watchex的key,值就是所有watchex這個key的client組成的乙個鍊錶。

無論在新增或者是刪除某個key時,都去檢查一下這個dict裡面,有沒有這個key。如果有,取出所有的client,發乙份通知訊息。

由於這個watchex這個命令,是乙個典型的非同步通知。所以在客戶端呼叫這個命令時,要使用redis的非同步執行命令介面redisasynccommand。具體呼叫方式,可以參考example-ae.c檔案。

當然在客戶端解析請求時,也要做一些變化。在async.c這個檔案中,redisprocesscallbacks()這個函式專門解析伺服器發回來的相應。每次從讀緩衝區組裝出乙個redisreply結構,然後從rediscallbacklist 裡面取出頭結點,其實就是乙個**函式,將redisreply傳入到這個**函式。這就是一次正常的呼叫過程。但是對於watchex命令,它是乙個永久命令,故而不能**函式不能插到rediscallbacklist裡面,所以另外建了乙個dict用於儲存watchex命令的**函式,鍵是watchex命令的key,值即是**函式。這樣每次客戶端解析出乙個redisreply,首先判斷這個reply是不是乙個watchex命令的返回,如果是就從dict裡面獲取相應的**函式,否則執行原有的解析流程。

整個過程即是如此,那麼下面我們說一下在此基礎上實現分布式鎖的過程

首先,呼叫settp key "value"命令,如果返回成功,則說明獲取鎖成功;否則呼叫watchex key命令。由於這兩步操作不是原子的,所以有可能呼叫watchex命令之後,返回noexist ,那麼這時可以再嘗試呼叫settp命令。如果還返回失敗,說明鎖已經被其他人占有,呼叫者可以等待或者幹別的事。 當占有鎖的人,用完釋放之後,所有watchex這個key的client都會收到通知,這時所有client都會呼叫settp命令去搶鎖,只會有乙個人成功,其餘的則繼續等待,直到能搶占到鎖為止。

redis分布式鎖可靠嗎

寫在前面的話 2020年2月22日來杭,杭州天氣不錯,晴空萬里,氣溫回暖,疫情彷佛散去,而我開始了既定的跳槽,投簡歷,刷面試片刻未敢停留。一周下來也差不多面了10來家公司,反饋還行,但是並沒有想象中的那麼好,總體來看杭州網際網路既沒有那麼好,也沒有想象的那麼槽。所以小夥伴們適度焦慮就ok,重要的還是...

用redis構建分布式鎖

從2.6.12版本開始,redis為set命令增加了一系列選項 如果有2個程序 可能位於不同機器 需要競爭某個資源,可以為這個資源加鎖,鎖放在redis裡面,這樣兩個程序都能訪問到,例如下面的命令 set resource name random value nx ex max lock time ...

分布式鎖 使用Redis實現分布式鎖

關於分布式鎖的實現,我的前一篇文章講解了如何使用zookeeper實現分布式鎖。關於分布式鎖的背景此處不再做贅述,我們直接討論下如何使用redis實現分布式鎖。關於redis,筆主不打算做長篇大論的介紹,只介紹下redis優秀的特性。支援豐富的資料型別,如string list map set zs...