久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

使用Redis如何實現分布式鎖

201次閱讀
沒有評論

共計 2431 個字符,預計需要花費 7 分鐘才能閱讀完成。

這篇文章主要介紹了使用 Redis 如何實現分布式鎖的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇使用 Redis 如何實現分布式鎖文章都會有所收獲,下面我們一起來看看吧。

1. 什么是分布式鎖

當我們在編寫多線程代碼的時候,不同的線程可能會發生資源的爭奪,為了避免資源爭奪造成的錯誤,我們會對資源上鎖,只有獲得鎖的線程才能繼續往下執行。

進程中的鎖,本質就是內存中一個變量,當一個線程執行某個操作申請加鎖時,如果能成功把代表鎖的變量值設置為 1,則表示獲得了鎖,其他線程想要獲得鎖時會阻塞,而擁有鎖的線程執行完操作后,再把鎖的值設置為 0,則表示釋放了鎖。

上面我們說的是在一臺服務器的進程內不同線程之間的鎖,這個鎖是放在內存中的,而對于分布式應用程序來說,不同的應用 (進程或線程) 部署在不同的服務器上,這樣就不能通過內存中的變量來表示鎖。

即然在一臺服務器上可以通過內存這塊共享的空間來表示鎖,那么對于分布式應用程序來說,可以共享存儲系統來存儲一個共享鎖,這就是分布式鎖,而 Redis 作為內存數據庫,執行非常快,很適合作為實現分布式鎖的共享存儲系統。

2. 使用 Redis 實現分布式鎖

對于一個鎖來說,其實只有兩個操作,加鎖和釋放鎖,下面我們看來看通過 Redis 要怎么實現?

2.1 加鎖

Redis 的 setnx 命令會判斷鍵值是否存在,如果存在則不做任何操作,并返回 0,如果不存在,則創建并賦值,并返回 1,因此我們可以執行 setnx 為一個代表鎖鍵設置值,如果能設置成功,則表示獲得鎖,失敗則無法獲得鎖。

#  使用 key 為 lock 來表示一個鎖
setnx lock 1

2.2 釋放鎖

當執行好操作之后,要釋放鎖的時候直接把 Redis 里的鍵值 lock 刪除就可以了,這樣其他進程才能通過 setnx 命令重新設置并獲得該鎖。

#  釋放鎖
del lock

通過上面兩個命令,我們實現了一個簡單的分布式鎖,但這里就出現了一個問題:如果一個進程通過 setnx 命令加鎖之后,在執行具體操作出錯了,沒有辦法及時釋放鎖,那么其他進程就無法獲得該鎖,系統便無法繼續往下執行,解決這個問題的辦法就是為鎖設置一個有效期,在這個有效期之后,自動釋放鎖。

2.3 給鎖設置有效期

給鎖設置有效期非常簡單,直接使用 Redis 的 expire 命令就可以了,如:

#  加鎖
setnx lock 1 
#  給鎖設置 10s 有效期
expire lock 10

但是,現在又出現另一個問題了,如果我們在設置了鎖之后,執行 expire 命令之前該進程掛掉了,那么 expire 就沒有執行成功,鎖一樣是沒有被釋放掉的,所以一定要保證上面兩個命令要一起執行,怎么保證呢?

有兩個方法,一個是使用 LUA 語言編寫的腳本,另一個是使用 Redis 的 set 命令,set 命令后面跟 nx 參數后,執行的效果與 setnx 一致,且 set 命令可以跟 ex 參數來設置過期時間,所以我們可以使用 set 命令把 setnx 和 expire 兩個合并在一起,這樣就可以保證執行的原子性了。

#  判斷是否鍵值是否存在,ex 后面跟著的是鍵值的有效期,10s
set lock 1 nx ex 10

解決了鎖的有效問題,現在我們再來看另外一個問題。

如上圖所示,現在有 A,B,C 三個不同服務器上的進程在執行某個操作都需要獲得鎖,執行后要釋放鎖。

現在的情況是進程 A 執行第 2 步時卡頓了(上面綠色區域所示),且時間超出了鎖有效期,所以進程 A 設置的鎖自動釋放了,這時候進程 B 獲得了鎖,并開始執行操作,但由于進程 A 只是卡頓了而已,所以會繼續執行的時候,在第 3 步的時候會手動釋放鎖,但是這個時候,鎖由線程 B 所擁有,也就是說進程 A 刪除的不是自己的鎖,而進程 B 的鎖,這時候進程 B 還沒執行完,但鎖被釋放后,進程 C 可以加鎖,也就是說由于進程 A 卡頓釋放錯了鎖,導致進程 B 和進程 C 可以同時獲得鎖。

怎么避免這種情況呢?如何區分其他進程的鎖,避免刪除其他進程的鎖呢?答案就是每個進程在加鎖的時候,給鎖設置一個唯一值,并在釋放鎖的時候,判斷是不是自己設置的鎖。

2.4 給鎖設置唯一值

給鎖設置唯一值的時候,一樣是使用 set 命令,唯一的不同是將鍵值 1 改為一個隨機生成的唯一值,比如 uuid。

 # rand_uid 表示唯一 id
set lock rand_id nx ex 10

當鎖里的值由進程設置后,釋放鎖的時候,就需要判斷鎖是不是自己的,步驟如下:

通過 Redis 的 get 命令獲得鎖的值

根據獲得的值,判斷鎖是不是自己設置的

如果是,通過 del 命令釋放鎖。

此時我們看到,釋放鎖需要執行三個操作,如果三個操作依次執行的話,是沒有辦法保證原子性的,比如進程 A 在執行到第 2 步后,準備開始執行 del 命令時,而鎖由時有效期到了,被自動釋放了,并被其他服務器上的進程 B 獲得鎖,但這時候線程 A 執行 del 還是把線程 B 的鎖給刪掉了。

解決這個問題的辦法就是保證上述三個操作執行的原子性,即在執行釋放鎖的三個操作中,其他進程不可以獲得鎖,想要做到這一點,需要使用到 LUA 腳本。

2.5 通過 LUA 腳本實現釋放鎖的原子性

Redis 支持 LUA 腳本,LUA 腳里的代碼執行的時候,其他客戶端的請求不會被執行,這樣可以保證原子性操作,所以我們可以使用下面腳本進行鎖的釋放:

if redis.call(get ,KEYS[1]) == ARGV[1] then 
 return redis.call(del ,KEYS[1])
else 
 return 0
end

將上述腳本保存為腳本后,可以調用 Redis 客戶端命令 redis-cli 來執行,如下:

# lock 為 key,rand_id 表示 key 里保存的值
redis-cli --eval unlock.lua lock , rand_id

關于“使用 Redis 如何實現分布式鎖”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“使用 Redis 如何實現分布式鎖”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注丸趣 TV 行業資訊頻道。

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-07-13發表,共計2431字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 文水县| 贺兰县| 龙口市| 海南省| 石家庄市| 犍为县| 西和县| 两当县| 托克逊县| 德江县| 会同县| 济阳县| 延庆县| 永德县| 敖汉旗| 凤凰县| 房山区| 邵阳市| 谢通门县| 偃师市| 东兴市| 玉门市| 法库县| 福清市| 福建省| 寻乌县| 博罗县| 阳西县| 开封市| 哈尔滨市| 永丰县| 海伦市| 濮阳市| 玉门市| 景洪市| 东源县| 化隆| 白玉县| 烟台市| 丹凤县| 郯城县|