淺析Redis中紅鎖RedLock的實現(xiàn)原理
RedLock 是一種分布式鎖的實現(xiàn)算法,由 Redis 的作者 Salvatore Sanfilippo(也稱為 Antirez)提出,主要用于解決在分布式系統(tǒng)中實現(xiàn)可靠鎖的問題。在 Redis 單獨節(jié)點的基礎上,RedLock 使用了多個獨立的 Redis 實例(通常建議是奇數(shù)個,比如 5 個),共同協(xié)作來提供更強健的分布式鎖服務。
RedLock 算法旨在解決單個 Redis 實例作為分布式鎖時可能出現(xiàn)的單點故障問題,通過在多個獨立運行的 Redis 實例上同時獲取鎖的方式來提高鎖服務的可用性和安全性。
RedLock 具備以下主要特性:
- 互斥性:在任何時間,只有一個客戶端可以獲得鎖,確保了資源的互斥訪問。
- 避免死鎖:通過為鎖設置一個較短的過期時間,即使客戶端在獲得鎖后由于網(wǎng)絡故障等原因未能按時釋放鎖,鎖也會因為過期而自動釋放,避免了死鎖的發(fā)生。
- 容錯性:即使一部分 Redis 節(jié)點宕機,只要大多數(shù)節(jié)點(即過半數(shù)以上的節(jié)點)仍在線,RedLock 算法就能繼續(xù)提供服務,并確保鎖的正確性。
1.RedLock 實現(xiàn)思路
RedLock 是對集群的每個節(jié)點進行加鎖,如果大多數(shù)節(jié)點(N/2+1)加鎖成功,則才會認為加鎖成功。
這樣即使集群中有某個節(jié)點掛掉了,因為大部分集群節(jié)點都加鎖成功了,所以分布式鎖還是可以繼續(xù)使用的。
2.工作流程
RedLock 算法的工作流程大致如下:
- 客戶端向多個獨立的 Redis 實例嘗試獲取鎖,設置鎖的過期時間非常短。
- 如果客戶端能在大部分節(jié)點上成功獲取鎖,并且所花費的時間小于鎖的過期時間的一半,那么認為客戶端成功獲取到了分布式鎖。
- 當客戶端完成對受保護資源的操作后,它需要向所有曾獲取鎖的 Redis 實例釋放鎖。
- 若在釋放鎖的過程中,客戶端因故無法完成,由于設置了鎖的過期時間,鎖最終會自動過期釋放,避免了死鎖。
3.基本使用
在 Java 開發(fā)中,可以使用 Redisson 框架很方便的實現(xiàn) RedLock,具體操作代碼如下:
import org.redisson.Redisson; import org.redisson.api.RedisClient; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.redisson.redisson.RedissonRedLock; public class RedLockDemo { public static void main(String[] args) { // 創(chuàng)建 Redisson 客戶端配置 Config config = new Config(); config.useClusterServers() .addNodeAddress("redis://127.0.0.1:6379", "redis://127.0.0.1:6380", "redis://127.0.0.1:6381"); // 假設有三個 Redis 節(jié)點 // 創(chuàng)建 Redisson 客戶端實例 RedissonClient redissonClient = Redisson.create(config); // 創(chuàng)建 RedLock 對象 RedissonRedLock redLock = redissonClient.getRedLock("resource"); try { // 嘗試獲取分布式鎖,最多嘗試 5 秒獲取鎖,并且鎖的有效期為 5000 毫秒 boolean lockAcquired = redLock.tryLock(5, 5000, TimeUnit.MILLISECONDS); if (lockAcquired) { // 加鎖成功,執(zhí)行業(yè)務代碼... } else { System.out.println("Failed to acquire the lock!"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println("Interrupted while acquiring the lock"); } finally { // 無論是否成功獲取到鎖,在業(yè)務邏輯結(jié)束后都要釋放鎖 if (redLock.isLocked()) { redLock.unlock(); } // 關(guān)閉 Redisson 客戶端連接 redissonClient.shutdown(); } } }
4.實現(xiàn)原理
Redisson 中的 RedLock 是基于 RedissonMultiLock(聯(lián)鎖)實現(xiàn)的。
RedissonMultiLock 是 Redisson 提供的一種分布式鎖類型,它可以同時操作多個鎖,以達到對多個鎖進行統(tǒng)一管理的目的。聯(lián)鎖的操作是原子性的,即要么全部鎖住,要么全部解鎖。這樣可以保證多個鎖的一致性。
RedissonMultiLock 使用示例如下:
import org.redisson.Redisson; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.redisson.multi.MultiLock; public class RedissonMultiLockDemo { public static void main(String[] args) throws InterruptedException { // 創(chuàng)建 Redisson 客戶端 Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379"); RedissonClient redisson = Redisson.create(config); // 創(chuàng)建多個分布式鎖實例 RLock lock1 = redisson.getLock("lock1"); RLock lock2 = redisson.getLock("lock2"); RLock lock3 = redisson.getLock("lock3"); // 創(chuàng)建 RedissonMultiLock 對象 MultiLock multiLock = new MultiLock(lock1, lock2, lock3); // 加鎖 multiLock.lock(); try { // 執(zhí)行任務 System.out.println("Lock acquired. Task started."); Thread.sleep(3000); System.out.println("Task finished. Releasing the lock."); } finally { // 釋放鎖 multiLock.unlock(); } // 關(guān)閉客戶端連接 redisson.shutdown(); } }
在示例中,我們首先創(chuàng)建了一個 Redisson 客戶端并連接到 Redis 服務器。然后,我們使用 redisson.getLock 方法創(chuàng)建了多個分布式鎖實例。接下來,我們通過傳入這些鎖實例來創(chuàng)建了 RedissonMultiLock 對象。
說回正題,RedissonRedLock 是基于 RedissonMultiLock 實現(xiàn)的這點,可以從繼承關(guān)系看出。
RedissonRedLock 繼承自 RedissonMultiLock,核心實現(xiàn)源碼如下:
public class RedissonRedLock extends RedissonMultiLock { public RedissonRedLock(RLock... locks) { super(locks); } /** * 鎖可以失敗的次數(shù),鎖的數(shù)量-鎖成功客戶端最小的數(shù)量 */ @Override protected int failedLocksLimit() { return locks.size() - minLocksAmount(locks); } /** * 鎖的數(shù)量 / 2 + 1,例如有3個客戶端加鎖,那么最少需要2個客戶端加鎖成功 */ protected int minLocksAmount(final List<RLock> locks) { return locks.size()/2 + 1; } /** * 計算多個客戶端一起加鎖的超時時間,每個客戶端的等待時間 */ @Override protected long calcLockWaitTime(long remainTime) { return Math.max(remainTime / locks.size(), 1); } @Override public void unlock() { unlockInner(locks); } }
從上述源碼可以看出,Redisson 中的 RedLock 是基于 RedissonMultiLock(聯(lián)鎖)實現(xiàn)的,當 RedLock 是對集群的每個節(jié)點進行加鎖,如果大多數(shù)節(jié)點,也就是 N/2+1 個節(jié)點加鎖成功,則認為 RedLock 加鎖成功。
5.存在問題
RedLock 主要存在以下兩個問題:
性能問題:RedLock 要等待大多數(shù)節(jié)點返回之后,才能加鎖成功,而這個過程中可能會因為網(wǎng)絡問題,或節(jié)點超時的問題,影響加鎖的性能。
并發(fā)安全性問題:當客戶端加鎖時,如果遇到 GC 可能會導致加鎖失效,但 GC 后誤認為加鎖成功的安全事故,例如以下流程:
客戶端 A 請求 3 個節(jié)點進行加鎖。
在節(jié)點回復處理之前,客戶端 A 進入 GC 階段(存在 STW,全局停頓)。
之后因為加鎖時間的原因,鎖已經(jīng)失效了。
客戶端 B 請求加鎖(和客戶端 A 是同一把鎖),加鎖成功。
客戶端 A GC 完成,繼續(xù)處理前面節(jié)點的消息,誤以為加鎖成功。
此時客戶端 B 和客戶端 A 同時加鎖成功,出現(xiàn)并發(fā)安全性問題。
6.已廢棄的 RedLock
因為 RedLock 存在的問題爭議較大,且沒有完美的解決方案,所以 Redisson 中已經(jīng)廢棄了 RedLock,這一點在 Redisson 官方文檔中能找到,如下圖所示:
課后思考
既然 RedLock 已經(jīng)被廢棄,那么想要實現(xiàn)分布式鎖,同時又想避免 Redis 單點故障問題,應該使用哪種解決方案呢?
到此這篇關(guān)于淺析Redis中紅鎖RedLock的實現(xiàn)原理的文章就介紹到這了,更多相關(guān)Redis紅鎖RedLock內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用redis實現(xiàn)聊天記錄轉(zhuǎn)存功能的全過程
社交類軟件聊天功能必不可少,聊天記錄存儲的方式也比較多,比如文本,數(shù)據(jù)庫,云等等,但是最好的選擇還是redis進行存儲,這篇文章主要給大家介紹了關(guān)于如何利用redis實現(xiàn)聊天記錄轉(zhuǎn)存功能的相關(guān)資料,需要的朋友可以參考下2021-08-08Redis使用bloom-filter過濾器實現(xiàn)推薦去重
這篇文章主要介紹了Redis使用bloom-filter過濾器實現(xiàn)推薦去重,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-11-11關(guān)于redis可視化工具讀取數(shù)據(jù)亂碼問題
大家來聊一聊在日常操作redis時用的是什么工具,redis提供的一些命令你都了解了嗎,今天通過本文給大家介紹redis可視化工具讀取數(shù)據(jù)亂碼問題,感興趣的朋友跟隨小編一起看看吧2021-07-07