Redis 如何實現分布式鎖?
首先來說 Redis 作為一個獨立的三方系統(tǒng),其天生的優(yōu)勢就是可以作為一個分布式系統(tǒng)來使用,因此使用 Redis 實現的鎖都是分布式鎖,理解了這個概念才能看懂本文所說的內容。
分布式鎖的示意圖,如下所示:
分布式鎖實現
使用 Redis 實現分布式鎖,可以通過 setnx(set if not exists)命令實現,當我們使用 setnx 創(chuàng)建鍵值成功時,則表明加鎖成功,否則既代碼加鎖失敗。因為 Redis 主線程是單線程運行的,所以也不會有同時加鎖成功的情況。實現命令如下:
127.0.0.1:6379> setnx lock true
(integer) 1 #創(chuàng)建鎖成功
#邏輯業(yè)務處理...
127.0.0.1:6379> del lock
(integer) 1 #釋放鎖
當我們重復加鎖時執(zhí)行結果如下:
127.0.0.1:6379> setnx lock true # 第一次加鎖
(integer) 1
127.0.0.1:6379> setnx lock true # 第二次加鎖
(integer) 0
從上述命令中可以看出,我們可以使用執(zhí)行的結果是否為 1 來判斷加鎖是否成功。
分布鎖問題
然而,使用 setnx 實現分布鎖有一個【死鎖問題】,就是當加鎖的線程(或應用)掉電或崩潰之后,其他線程只能無限等待下去的問題。
此時,我們解決死鎖問題可以通過添加設置鎖的過期時間來實現。也就是 setnx 和 expire 配合使用,在 Redis 2.6.12 版本之后,新增了一個強大的功能,我們可以使用一個原子操作也就是一條命令來執(zhí)行 setnx 和 expire 操作了,實現命令如下:
127.0.0.1:6379> set lock true ex 30 nx
OK #創(chuàng)建鎖成功
127.0.0.1:6379> set lock true ex 30 nx
(nil) #在鎖被占用的時候,企圖獲取鎖失敗
其中 ex 為設置超時時間, nx 為元素非空判斷,用來判斷是否能正常使用鎖的。
小結
Redis 作為一個獨立的三方系統(tǒng),其優(yōu)勢是天生可以實現分布式鎖。它實現分布式鎖是通過 setnx 來實現的,然而只有 nx(not exists)可能會發(fā)生死鎖的問題,所以我們最終實現死鎖應該使用 set nx ex 的方式來實現。
小思考:當設置了過期時間之后,Redis 實現分布式依然會有一個小小的問題,有人知道這個問題和解決方案嗎?歡迎評論區(qū)寫出你的答案。此文章點贊過 100,我會給出標準答案。
本文已收錄到我的面試小站 www.javacn.site,其中包含的內容有:Redis、JVM、并發(fā)、并發(fā)、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、設計模式、消息隊列等模塊。