天天看點

redis cluster 分布式鎖_Redisson實作Redis分布式鎖的N種姿勢

點選藍色“程式猿DD”關注我喲

redis cluster 分布式鎖_Redisson實作Redis分布式鎖的N種姿勢

來源:阿飛的部落格

前幾天發的一篇文章《Redlock:Redis分布式鎖最牛逼的實作》,引起了一些同學的讨論,也有一些同學提出了一些疑問,這是好事兒。本文在講解如何使用Redisson實作Redis普通分布式鎖,以及Redlock算法分布式鎖的幾種方式的同時,也附帶解答這些同學的一些疑問。

Redis幾種架構

Redis發展到現在,幾種常見的部署架構有:

  1. 單機模式;
  2. 主從模式;
  3. 哨兵模式;
  4. 叢集模式;

我們首先基于這些架構講解Redisson普通分布式鎖實作,需要注意的是,隻有充分了解普通分布式鎖是如何實作的,才能更好的了解Redlock分布式鎖的實作,因為Redlock分布式鎖的實作完全基于普通分布式鎖。

普通分布式鎖

Redis普通分布式鎖原理這個大家基本上都了解,本文不打算再過多的介紹,上一篇文章《Redlock:Redis分布式鎖的實作》也講的很細,并且也說到了幾個重要的注意點。如果你對Redis普通的分布式鎖還有一些疑問,可以再回顧一下這篇文章。

接下來直接show you the code,畢竟 talk is cheap。

  • redisson版本

本次測試選擇redisson 2.14.1版本。

單機模式

源碼如下:

通過代碼可知,經過Redisson的封裝,實作Redis分布式鎖非常友善,我們再看一下Redis中的value是啥,和前文分析一樣,hash結構,key就是資源名稱,field就是UUID+threadId,value就是重入值,在分布式鎖時,這個值為1(Redisson還可以實作重入鎖,那麼這個值就取決于重入次數了):

哨兵模式

即sentinel模式,實作代碼和單機模式幾乎一樣,唯一的不同就是Config的構造:

叢集模式

叢集模式構造Config如下:

總結

普通分布式實作非常簡單,無論是那種架構,向Redis通過EVAL指令執行LUA腳本即可。

Redlock分布式鎖

那麼Redlock分布式鎖如何實作呢?以單機模式Redis架構為例,直接看實作代碼:

最核心的變化就是

RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);

,因為我這裡是以三個節點為例。

那麼如果是哨兵模式呢?需要搭建3個,或者5個sentinel模式叢集(具體多少個,取決于你)。

那麼如果是叢集模式呢?需要搭建3個,或者5個cluster模式叢集(具體多少個,取決于你)。

實作原理

既然核心變化是使用了RedissonRedLock,那麼我們看一下它的源碼有什麼不同。這個類是RedissonMultiLock的子類,是以調用tryLock方法時,事實上調用了RedissonMultiLock的tryLock方法,精簡源碼如下:

public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
    // 實作要點之允許加鎖失敗節點限制(N-(N/2+1))
    int failedLocksLimit = failedLocksLimit();
    List acquiredLocks = new ArrayList(locks.size());// 實作要點之周遊所有節點通過EVAL指令執行lua加鎖for (ListIterator iterator = locks.listIterator(); iterator.hasNext();) {
        RLock lock = iterator.next();
        boolean lockAcquired;try {// 對節點嘗試加鎖
            lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS);
        } catch (RedisConnectionClosedException|RedisResponseTimeoutException e) {// 如果抛出這類異常,為了防止加鎖成功,但是響應失敗,需要解鎖
            unlockInner(Arrays.asList(lock));
            lockAcquired = false;
        } catch (Exception e) {// 抛出異常表示擷取鎖失敗
            lockAcquired = false;
        }if (lockAcquired) {// 成功擷取鎖集合
            acquiredLocks.add(lock);
        } else {// 如果達到了允許加鎖失敗節點限制,那麼break,即此次Redlock加鎖失敗if (locks.size() - acquiredLocks.size() == failedLocksLimit()) {break;
            }               
        }
    }return true;
}
           

很明顯,這段源碼就是上一篇文章《Redlock:Redis分布式鎖的實作》提到的Redlock算法的完全實作。

以sentinel模式架構為例,如下圖所示,有sentinel-1,sentinel-2,sentinel-3總計3個sentinel模式叢集,如果要擷取分布式鎖,那麼需要向這3個sentinel叢集通過EVAL指令執行LUA腳本,需要3/2+1=2,即至少2個sentinel叢集響應成功,才算成功的以Redlock算法擷取到分布式鎖:

redis cluster 分布式鎖_Redisson實作Redis分布式鎖的N種姿勢

Redlock分布式鎖

問題合集

redis cluster 分布式鎖_Redisson實作Redis分布式鎖的N種姿勢

image.png

根據上面實作原理的分析,這位同學應該是對Redlock算法實作有一點點誤解,假設我們用5個節點實作Redlock算法的分布式鎖。那麼要麼是5個redis單執行個體,要麼是5個sentinel叢集,要麼是5個cluster叢集。而不是一個有5個主節點的cluster叢集,然後向每個節點通過EVAL指令執行LUA腳本嘗試擷取分布式鎖,如上圖所示。

  • 失效時間如何設定

這個問題的場景是,假設設定失效時間10秒,如果由于某些原因導緻10秒還沒執行完任務,這時候鎖自動失效,導緻其他線程也會拿到分布式鎖。

這确實是Redis分布式最大的問題,不管是普通分布式鎖,還是Redlock算法分布式鎖,都沒有解決這個問題。也有一些文章提出了對失效時間續租,即延長失效時間,很明顯這又提升了分布式鎖的複雜度。另外就筆者了解,沒有現成的架構有實作,如果有哪位知道,可以告訴我,萬分感謝。

  • redis分布式鎖的高可用

關于Redis分布式鎖的安全性問題,在分布式系統專家Martin Kleppmann和Redis的作者antirez之間已經發生過一場争論。有興趣的同學,搜尋"基于Redis的分布式鎖到底安全嗎"就能得到你想要的答案,需要注意的是,有上下兩篇(這應該就是傳說中的神仙打架吧,哈)。

  • zookeeper or redis

沒有絕對的好壞,隻有更适合自己的業務。就性能而言,redis很明顯優于zookeeper;就分布式鎖實作的健壯性而言,zookeeper很明顯優于redis。如何選擇,取決于你的業務!

号外:最近整理了之前編寫的一系列内容做成了PDF,關注我并回複相應密碼擷取:

- 001 :領取《Spring Boot基礎教程》

- 002 :領取《Spring Cloud基礎教程》

更多内容陸續奉上,敬請期待 

redis cluster 分布式鎖_Redisson實作Redis分布式鎖的N種姿勢

- END -

 近期熱文:

  • 為什麼美國程式員工作比中國程式員工作輕松、加班少?
  • Spring Cloud Alibaba 基礎教程整理

  • 我為啥不看好ServiceMesh

  • 靈活團隊的病與藥

  • 分布式系統關注點:彈性架構

  • GitHub 尋寶指南

  • 虎牙直播在微服務改造方面的實踐和總結

  • 有贊搜尋系統的架構演進

  • 在前後端分離的路上承受了多少痛?

  • 中台是個什麼鬼?

redis cluster 分布式鎖_Redisson實作Redis分布式鎖的N種姿勢

看完,趕緊點個“好看”鴨

點鴨點鴨

                                                                                                       ↓↓↓↓

繼續閱讀