精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

深度剖析 Redisson 分布式鎖:原理、實(shí)現(xiàn)與應(yīng)用實(shí)踐

開發(fā)
Redisson 不僅僅是一個簡單的分布式鎖工具,它更像是一套完整的分布式協(xié)調(diào)框架,提供了豐富多樣的分布式對象和服務(wù),極大地簡化了分布式系統(tǒng)的開發(fā)過程。

在當(dāng)今分布式系統(tǒng)大行其道的技術(shù)領(lǐng)域,如何有效協(xié)調(diào)多個節(jié)點(diǎn)之間對共享資源的訪問,成了開發(fā)者們必須攻克的一道難關(guān)。分布式鎖,作為解決這一難題的關(guān)鍵技術(shù)手段,正發(fā)揮著舉足輕重的作用。

在眾多分布式鎖的實(shí)現(xiàn)方案中,Redisson 以其強(qiáng)大的功能、出色的性能和極高的易用性脫穎而出,成為了開發(fā)者們的得力助手。Redisson 不僅僅是一個簡單的分布式鎖工具,它更像是一套完整的分布式協(xié)調(diào)框架,提供了豐富多樣的分布式對象和服務(wù),極大地簡化了分布式系統(tǒng)的開發(fā)過程。

一、詳解Redisson 分布式鎖使用和實(shí)現(xiàn)

1. 前置集成Redisson 基礎(chǔ)配置

使用Redisson時我們優(yōu)先需要引入其依賴:

<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.23.5</version>
        </dependency>

然后配置Redis基本配置信息,這里筆者以單體架構(gòu)為例給出redis的配置示例:

spring.redis.host=localhost
spring.redis.port=6379

2. 分布式鎖的基本使用

RLock繼承了JUC包下的Lock接口,所以使用起來和JUC包下的幾個lock類似,這里我們也給出相應(yīng)的基本代碼示例:

CountDownLatch countDownLatch = new CountDownLatch(2);


        //聲明一把分布式鎖
        RLock lock = redissonClient.getLock("lock");

        new Thread(() -> {
            try {
                //上鎖
                lock.lock();

                log.info("lock lock success");
                ThreadUtil.sleep(1, TimeUnit.MINUTES);

                countDownLatch.countDown();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }).start();


        new Thread(() -> {
            try {
                //休眠5s讓上一個線程先取鎖
                ThreadUtil.sleep(5, TimeUnit.SECONDS);

                //上鎖
                if (lock.tryLock()) {
                    log.info("try  lock success");
                    //成功后執(zhí)行業(yè)務(wù)邏輯然后釋放鎖
                    ThreadUtil.sleep(1, TimeUnit.MINUTES);
                    lock.unlock();
                } else {
                    log.info("try lock fail");
                }

                countDownLatch.countDown();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        countDownLatch.await();

對應(yīng)的輸出結(jié)果如下,可以看到第一個線程基于redisson上鎖成功后,第二個線程就無法上鎖了:

3. 公平鎖的使用

默認(rèn)情況下Redisson分布式鎖是非公平的,即任意時刻任意一個請求都可以在鎖釋放后爭搶分布式鎖,對此redisson給出了公平鎖的實(shí)現(xiàn),如下代碼所示,筆者通過getFairLock聲明一把公平鎖,讓聲明5個線程進(jìn)行爭搶:

int size = 5;
        //聲明分布式鎖
        RLock reentrantLock = redissonClient.getFairLock("lock");
        //創(chuàng)建線程池
        ExecutorService threadPool = Executors.newFixedThreadPool(size);

        CountDownLatch countDownLatch = new CountDownLatch(size);

        //遍歷線程池,讓池內(nèi)的線程爭搶分布式鎖
        for (int i = 0; i < size; i++) {
            threadPool.submit(() -> {
                try {
                    reentrantLock.lock();
                    log.info("reentrantLock.lock success");
                } catch (Exception e) {
                    log.error("reentrantLock.lock error", e);
                } finally {
                    reentrantLock.unlock();
                    log.info("reentrantLock.unlock success");
                    countDownLatch.countDown();
                }
            });
        }

        countDownLatch.await();

可以看到,筆者通過調(diào)試的方式順序讓線程爭搶分布式鎖,最終輸出結(jié)果也是按照先來后到的方式獲取鎖和釋放鎖:

4. 聯(lián)鎖的使用

聯(lián)鎖顧名思義,只有一次性獲取多把鎖之后才能算成功,對應(yīng)的代碼示例如下:

RLock lock1 = redissonClient.getFairLock("lock-1");
        RLock lock2 = redissonClient.getFairLock("lock-2");
        RLock lock3 = redissonClient.getFairLock("lock-3");
        
        RedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2, lock3);
        try {
            // 同時加鎖:lock1 lock2 lock3
            // 所有的鎖都上鎖成功才算成功。
            boolean isLocked = multiLock.tryLock(1, TimeUnit.SECONDS);
            if (isLocked) {
                log.info("try lock success");
                multiLock.unlock();
            } else {
                log.info("try lock fail");
            }


        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

5. 讀寫鎖基本使用

Redisson也提供一把使用的分布式讀寫鎖,和常規(guī)的讀寫鎖一樣,redisson讀寫鎖也具備如下幾個特性:

  • 多個客戶端可以同時持有讀鎖不互斥。
  • 上了讀鎖之后,其他客戶端無法上寫鎖。
  • 上了寫鎖之后,其他客戶端無法上讀寫鎖。

總的來說,redisson讀寫鎖的特點(diǎn)就是寫與讀寫互斥,讀之間不互斥,對應(yīng)的我們也給出一段讀寫鎖的使用示例:

RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("readWriteLock");

        CountDownLatch countDownLatch = new CountDownLatch(4);


        new Thread(() -> {
            if (readWriteLock.writeLock().tryLock()) {
                log.info("try write lock success");
            } else {
                log.info("try write lock fail");
            }
            countDownLatch.countDown();
        }).start();


        new Thread(() -> {
            if (readWriteLock.writeLock().tryLock()) {
                log.info("try write lock success");
            } else {
                log.info("try write lock fail");
            }
            countDownLatch.countDown();
        }).start();


        new Thread(() -> {
            if (readWriteLock.readLock().tryLock()) {
                log.info("try read lock success");
            } else {
                log.info("try read lock fail");
            }
            countDownLatch.countDown();
        }).start();


        new Thread(() -> {
            if (readWriteLock.readLock().tryLock()) {
                log.info("try read lock success");
            } else {
                log.info("try read lock fail");
            }
            countDownLatch.countDown();
        }).start();


        countDownLatch.await();

從輸出結(jié)果可以看出,某個連接成功上了寫鎖之后,其他連接都無法持有這把鎖:

二、詳解Redisson常見問題

1. Redisson和Jedis有什么區(qū)別

(1) 分布式集合的支持:Redisson按照J(rèn)ava的語義和規(guī)范實(shí)現(xiàn)了各種java集合對象的實(shí)現(xiàn),包括multimap、priorityQueue、DelayQueue等設(shè)置是原子類,而Jedis僅僅支持一些比較常見的java集合類,例如Map、Set、List等。

(2) 分布式鎖和同步器:Redisson支持各種常見的java鎖和同步工具如FairLock、MultiLock、Semaphore、CountdownLatch等,而后者則都不支持。

(3) 分布式對象:Redisson支持各種publish/subscribe、bloomFilter、RateLimiter、Id generator等強(qiáng)大的功能,而后者僅僅支持java的原子類以及HyperLogLog等。

(4) 高級緩存特性:Redisson支持多種緩存功能,例如read-through/write-through/write-behind等,而后者不支持這些功能,具體可以參考:https://blog.csdn.net/HalfImmortal/article/details/106962943

(5) API架構(gòu):前者自持線程安全、異步接口、響應(yīng)式流接口和Rxjava3接口,而后者不支持。

(6) 分布式服務(wù):Redisson支持ExecutorService、MapReduce、SchedulerService等架構(gòu),而后者都不支持這些分布式服務(wù)。

(7) 框架支持:前者支持Spring Cache、hibernate Cache、Mybatis Cache,而后者僅僅支持Spring session和spring cache。

(8) Redisson和后者都支持認(rèn)證和ssl。

(9) 序列化:Redisson支持多種編碼和解碼器如json、jdk、avro等序列化,而后者僅僅支持json等簡單的序列化。

2. Redisson如何實(shí)現(xiàn)分布式鎖

該問題實(shí)際是兩個問題,即分布式和鎖,針對分布式問題,redisson底層已經(jīng)針對主從、集群等不同的架構(gòu)做了很好的封裝,可以較好的保證分布式架構(gòu)下鎖的單例。

再來說說鎖的問題,針對分布式取鎖這一功能點(diǎn),redisson上鎖的幾個邏輯分支為:

  • 判斷這把鎖是否存在,若不存在說明我們是第一個取鎖的,基于redis的hincrby指令創(chuàng)建這把鎖結(jié)構(gòu),key為鎖名稱,也就是我們的lock結(jié)構(gòu)為字典結(jié)構(gòu),field為我們這個線程名(這個線程是有隨機(jī)數(shù)的可以保證唯一),然后再通過pexpire設(shè)置這個當(dāng)前持有鎖的線程最大超時時間,以我們上述基礎(chǔ)示例那段代碼為例,對應(yīng)的指令就是:
hincrby lock 當(dāng)前取鎖的線程名 1
pexpire lock 當(dāng)前取鎖的線程名
  • 若發(fā)現(xiàn)鎖存在且通過hexists看到持有鎖的線程是我們當(dāng)前線程,說明本次是鎖重入,同樣基于hincrby 和pexpire 進(jìn)行鎖續(xù)約。
  • 若發(fā)現(xiàn)鎖存在且持有鎖的不是自己,則通過pttl得出持有鎖的線程的超期時間讓當(dāng)前上鎖失敗的線程按照自己的邏輯進(jìn)行進(jìn)一步處理。

有了上述的思路之后,redisson為了保證操作的原子性,將上述三個邏輯分支思路以lua腳本的形式進(jìn)行了進(jìn)一步的封裝,由此保證了分布式環(huán)境下上鎖操作的原子性:

對應(yīng)的我們也給出redisson對于這段代碼的核心實(shí)現(xiàn)部分,即位于RedissonLock的tryLockInnerAsync方法,邏輯和筆者說明的基本是一致的,讀者可以參考筆者的注釋了解一下細(xì)節(jié):

<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
        return commandExecutor.syncedEval(getRawName(), LongCodec.INSTANCE, command,
          //如果分布式鎖不存在,或者存在且持有鎖的是自己,則進(jìn)入if分支
                "if ((redis.call('exists', KEYS[1]) == 0) " +
                            "or (redis.call('hexists', KEYS[1], ARGV[2]) == 1)) then " +
                         //通過hincrby設(shè)置鎖持有者為當(dāng)前線程,這個鎖結(jié)構(gòu)是一個字典key為鎖的名稱,field為當(dāng)前線程,將這個field的對應(yīng)的value自增1
                        "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                        //設(shè)置鎖這個key的到期時間
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                    "end; " +
                    //如果發(fā)現(xiàn)鎖存在且持有鎖的不是自己則返回鎖的到期時間
                    "return redis.call('pttl', KEYS[1]);",
                Collections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));
    }

3. Redisson如何實(shí)現(xiàn)分布式鎖可重入

上一個問題中的分支2邏輯已經(jīng)說明了,即判斷鎖是否存在,如果存在且鎖的持有者是自己則自增一個自己持有鎖的次數(shù)標(biāo)識再次重入,對應(yīng)的lua腳本上一步已給出,讀者可回頭翻看一下:

4. Redisson如何實(shí)現(xiàn)公平鎖

公平鎖核心邏輯也是一個lua腳本,腳本比較長,筆者這里直接將這段腳本抽取出來逐步分析,首先來到下面這段腳本,在此之前我們先給出這段腳本對應(yīng)的參數(shù)說明方便后續(xù)的講解:

  • KEYS數(shù)組:該數(shù)組記錄了使用公平鎖所有涉及到的key信息,按照lua腳本的規(guī)范,索引是從1開始,按順序keys數(shù)組分別存儲的是如下數(shù)據(jù):
1. keys[1]:也就是我們的分布式鎖名稱,即lock
2. keys[2]:因?yàn)闆]上到鎖而進(jìn)入的等待隊(duì)列,key名稱為redisson_lock_queue:{lock},這個數(shù)據(jù)結(jié)構(gòu)筆者這里就稱之為等待隊(duì)列
3. keys[3]:記錄每個進(jìn)入等待隊(duì)列的線程需要等待的時間,key名稱為redisson_lock_timeout:{lock},這個列表我們就稱之為超時清單
  • ARGV[1]:記錄分布式鎖使用的租期,默認(rèn)是30s。
  • ARGV[2]:記錄當(dāng)前希望上鎖的線程名稱
  • ARGV[3]:指定上鎖的最大等待時長,即如果當(dāng)前上鎖失敗,線程進(jìn)入等待的時長,默認(rèn)為5min。
  • ARGV[4]:當(dāng)前時間

有了上述的參數(shù)的前置鋪墊之后,我們就可以開始逐段分析腳本的步驟,首先這段腳本會處于一個循環(huán)自旋,它只有在觸發(fā)如下兩個條件的時候跳出循環(huán):

  • 等待隊(duì)列中沒有元素了,說明當(dāng)前線程無需等待直接退出循環(huán)進(jìn)入后續(xù)步驟取鎖。
  • 查看當(dāng)前線程的等待超時時間,如果小于當(dāng)前時間則說明這個線程等待太長了,直接從等待列表和超時清單中移除。

對應(yīng)的lua代碼段如下:

while true do
 -- 查看等待隊(duì)列中是否存在元素,如果隊(duì)列為空,則說明無需等待直接退出循環(huán),嘗試拿鎖
    local firstThreadId2 = redis.call('lindex', KEYS[2], 0);
    if firstThreadId2 == false then
        break;
    end
    -- 如果等待隊(duì)列有元素,則到超時隊(duì)列KEYS[3]中獲取其超時時間并和ARGV[3]即當(dāng)前時間進(jìn)行比較,如果小于當(dāng)前時間則說明這個線程等待太長了,直接從超時清單和等待列表中移除
    local timeout = tonumber(redis.call('zscore', KEYS[3], firstThreadId2));
    if timeout <= tonumber(ARGV[4]) then
        redis.call('zrem', KEYS[3], firstThreadId2);
        redis.call('lpop', KEYS[2]);
    else
        break;
    end
end

結(jié)束上一步的循環(huán)之后,進(jìn)入如下邏輯:

  • 如果當(dāng)前沒有人持有鎖KEYS[1] 且等待隊(duì)列為空或者等待隊(duì)列沒有線程則將持有鎖的人設(shè)置為自己,并更新等待清單中其他線程的超時時間。
  • 如果當(dāng)前有人持有鎖且持有鎖的是當(dāng)前線程,則說明是重入,則通過hincrby到分布式鎖結(jié)構(gòu)中更新自己的上鎖次數(shù)為2,再通過pexpire延長持有鎖的到期時間。
-- 如果當(dāng)前沒有人持有鎖KEYS[1] 且等待隊(duì)列為空或者等待隊(duì)列沒有線程則進(jìn)入該邏輯
if (redis.call('exists', KEYS[1]) == 0) and ((redis.call('exists', KEYS[2]) == 0) or (redis.call('lindex', KEYS[2], 0) == ARGV[2])) then
 -- 將自己從等待隊(duì)列和超時清單中移除
    redis.call('lpop', KEYS[2]);
    redis.call('zrem', KEYS[3], ARGV[2]);
    -- 遍歷超時清單,更新等待清單中所有元素的等待時長
    local keys = redis.call('zrange', KEYS[3], 0, -1);
    for i = 1, #keys, 1 do
        redis.call('zincrby', KEYS[3], -tonumber(ARGV[3]), keys[i]);
    end
    -- 將持有鎖的線程設(shè)置為自己,上鎖次數(shù)為1
    redis.call('hset', KEYS[1], ARGV[2], 1);
    -- 設(shè)置自己持有鎖的時間為 ARGV[1]即30s,若30s后沒有續(xù)期則釋放該鎖
    redis.call('pexpire', KEYS[1], ARGV[1]);
    return nil;
end
-- 如果持有鎖的是自己,則增加重入次數(shù)并延長超時時間
if redis.call('hexists', KEYS[1], ARGV[2]) == 1 then
    redis.call('hincrby', KEYS[1], ARGV[2],1);
    redis.call('pexpire', KEYS[1], ARGV[1]);
    return nil;
end

如果沒有進(jìn)入上述步驟的分支中,則進(jìn)入下面這段判斷:

  • 從超時清單中查看是否有自己,如果有則獲取自己的超時時間,并減去當(dāng)前時間和等待時間獲該線程還需要等待的時長。
  • 若沒有則看看等待隊(duì)列中最后一個元素的超時時間,并基于這個超時時間獲取自己的等待時長,如果超時清單中沒有元素,則直接基于分布式鎖lock中持有鎖線程的到期時間獲取自己的等待時長。
  • 基于等待時長獲取自己的超時時間并將自己存入等待隊(duì)列和超時清單中。
-- 查看超時隊(duì)列是否有自己,如果有則返回還需等待的時間
local timeout = redis.call('zscore', KEYS[3], ARGV[2]);
if timeout ~= false then
    return timeout - tonumber(ARGV[3]) - tonumber(ARGV[4]);
end

-- 獲取等待隊(duì)列中最后一個線程
local lastThreadId = redis.call('lindex', KEYS[2], -1);
local ttl;
-- 如果該線程存在且不是自己,則基于該線程的等待超時時間減去當(dāng)前時間得到我們的線程還需要等待的時長
if lastThreadId ~= false and lastThreadId ~= ARGV[2] then
    ttl = tonumber(redis.call('zscore', KEYS[3], lastThreadId)) - tonumber(ARGV[4]);
else
 -- 如果等待隊(duì)列沒有元素,則直接到分布式鎖lock中獲取持有鎖的線程的超期時間得到自己的等待時長
    ttl = redis.call('pttl', KEYS[1]);
end
-- 基于上一步的ttl+等待時間+當(dāng)前時間得到超時時間并將自己存入等待列表和超時清單
local timeout = ttl + tonumber(ARGV[3]) + tonumber(ARGV[4]);
if redis.call('zadd', KEYS[3], timeout, ARGV[2]) == 1 then
    redis.call('rpush', KEYS[2], ARGV[2]);
end
return ttl;

經(jīng)過上一個步驟的逐步拆解分析,我們已經(jīng)將公平鎖的整體流程整理完成,來小結(jié)一下整體過程:

  • 循環(huán)等待其他線程釋放分布式鎖或者自己從等待清單中移除。
  • 判斷是否有人持有鎖,如果沒有則我們自己上鎖并設(shè)置超時時間,如果有且是自己則更新上鎖次數(shù)和續(xù)約時間,如果不符合這幾個要求進(jìn)入步驟3。
  • 查看超時清單中是否有自己,如果有則計(jì)算出還需要等待的時長并返回,如果沒有則進(jìn)入步驟4。
  • 從等待隊(duì)列中獲取最后一個等待的線程,基于它的等待時間計(jì)算出自己的等待時長并存入等待隊(duì)列和超時清單,反之進(jìn)入步驟5。
  • 來到這一步說明等待隊(duì)列沒有元素,直接基于分布式鎖中持有鎖的線程的到期時間設(shè)置自己的等待時間并入等待隊(duì)列和超時清單。

可以看出,redisson通過列表和超時清單按序管理了各個線程的等待實(shí)現(xiàn),保證了分布式鎖爭搶的公平性:

5. Redisson的watchdog機(jī)制是什么?底層是如何實(shí)現(xiàn)的?

redisson在設(shè)計(jì)初期考慮到客戶端因?yàn)楦鞣N客觀原因?qū)е骆i未能及時釋放導(dǎo)致其他連接無法持有鎖的情況提出了續(xù)期的概念,即客戶端上鎖后會默認(rèn)分配一個續(xù)期,在這段時間內(nèi)客戶端要定期向redis告知自己仍然需要這把鎖并進(jìn)行續(xù)約。

例如上文中的線程1持有鎖之后,會基于當(dāng)前時間+30s得出鎖到期時間,隨后在這個需求的三分之一也就是每隔10s向redis表明自己還存活著,不斷延長自己的到期時間,知道線程1完成后主動釋放這把鎖:

這也就意味著如果30s秒內(nèi),線程1出現(xiàn)以下情況,這把鎖就會被自動釋放:

  • 用戶主動設(shè)置超時時間,redission就不會自動續(xù)約
  • 沒有定期續(xù)命
  • 續(xù)期執(zhí)行失敗

在此之后,其他線程就可以搶鎖,由此避免了死鎖問題。這也就是我們常說的看門狗機(jī)制,這段代碼的實(shí)現(xiàn)可以在RedissonLock的tryAcquireOnceAsync方法中看到,通過tryLockInnerAsync完成上鎖并成功后,redisson就會基于當(dāng)前線程的信息通過scheduleExpirationRenewal提交一個定時續(xù)約的定時任務(wù):

private RFuture<Boolean> tryAcquireOnceAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
        CompletionStage<Boolean> acquiredFuture;
        
        if (leaseTime > 0) {
           //......
        } else {
         //提交一個異步搶分布式鎖的任務(wù)
            acquiredFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime,
                    TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
        }

        acquiredFuture = handleNoSync(threadId, acquiredFuture);
  //基于thenApply處理搶鎖任務(wù)的回調(diào)
        CompletionStage<Boolean> f = acquiredFuture.thenApply(acquired -> {
            // lock acquired
            if (acquired) {
                if (leaseTime > 0) {
                   //.....
                } else {
                 //如果上鎖成功則提交一個續(xù)約的定時任務(wù)
                    scheduleExpirationRenewal(threadId);
                }
            }
            return acquired;
        });
        return new CompletableFutureWrapper<>(f);
    }

我們步入scheduleExpirationRenewal即可看到該方法內(nèi)部的核心實(shí)現(xiàn)renewExpiration這個方法,可以看到該方法會基于續(xù)約時間的三分之一定期執(zhí)行renewExpirationAsync方法進(jìn)行續(xù)約:

private void renewExpiration() {
        //......
        //基于超時時間的三分之一生成一個定時任務(wù)
        Timeout task = getServiceManager().newTimeout(new TimerTask() {
            @Override
            public void run(Timeout timeout) throws Exception {
                //......
                //調(diào)用renewExpirationAsync執(zhí)行鎖續(xù)期
                CompletionStage<Boolean> future = renewExpirationAsync(threadId);
                future.whenComplete((res, e) -> {
                    //.....
                    //如果上鎖成功則遞歸提交一個renewExpiration等待下一次續(xù)約
                    if (res) {
                        // reschedule itself
                        renewExpiration();
                    } else {//如果上鎖失敗則釋放鎖
                        cancelExpirationRenewal(null);
                    }
                });
            }
        }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
        
        ee.setTimeout(task);
    }

最后我們給出renewExpirationAsync查看的續(xù)約的具體實(shí)現(xiàn),可以看到邏輯非常直觀:

  • 調(diào)用hexists查看分布式鎖的持有者是否是自己
  • 如果是則調(diào)用pexpire設(shè)置延長續(xù)期:
protected CompletionStage<Boolean> renewExpirationAsync(long threadId) {
        return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
          //查看持有鎖的是否是自己,其中KEYS[1]是Collections.singletonList(getRawName())即鎖的名稱
                "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                  //調(diào)用pexpire延長時間,KEYS[1]是Collections.singletonList(getRawName()),而ARGV[1]是internalLockLeaseTime
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return 1; " +
                        "end; " +
                        "return 0;",
                Collections.singletonList(getRawName()),
                internalLockLeaseTime, getLockName(threadId));
    }

6. 什么是RedLock,其實(shí)現(xiàn)思路是什么

紅鎖是redis作者Antirez提供的一個多節(jié)點(diǎn)分布式鎖的算法,主要用于解決集群環(huán)境下分布式鎖一致性問題,其做法大體思路如下::

  • 客戶端設(shè)置基于redis服務(wù)端獲取起始時間,并基于超時時間算出取鎖最長等待時間。
  • 基于這個時間點(diǎn)向redis集群節(jié)點(diǎn)發(fā)起上鎖請求。
  • 當(dāng)?shù)玫桨霐?shù)以上節(jié)點(diǎn)同一之后意為取鎖成功。

  • 執(zhí)行業(yè)務(wù)操作。
  • 完成后釋放鎖,注意這里釋放的操作不提供可靠釋放,僅僅向上鎖的節(jié)點(diǎn)發(fā)出釋放請求:

對此我們也給出redisson的使用示例:

RLock rLock1 = redissonClient1.getLock("lock1");
        RLock rLock2 = redissonClient2.getLock("lock2");
        RLock rLock3 = redissonClient3.getLock("lock3");
        RedissonRedLock redLock = new RedissonRedLock(rLock1, rLock2, rLock3);

        boolean lockResult = redLock.tryLock();
        if (lockResult) {
            try{
                //....
            } finally {
                redLock.unlock();
            }
        }

7. Redisson 中為什么要廢棄 RedLock

總體來說有以下幾個缺陷:

  • 缺乏認(rèn)證
  • 維護(hù)和操作復(fù)雜
  • 被分布式系統(tǒng)指明研究者M(jìn)artin 批評,指明某些場景不能正確提供鎖服務(wù)。
  • 存在安全漏洞

這里我們針對第3點(diǎn)進(jìn)行相應(yīng)的補(bǔ)充,按照Antirez的說法,red lock實(shí)際上是無法在NPC三種異常情況做出正確響應(yīng),而NPC對應(yīng)含義是:

  • N(Network Delay):網(wǎng)絡(luò)延遲
  • P(Process Pause):進(jìn)程暫停
  • C(Clock Drift):時鐘飄逸

他基于反證法提出了下面兩個場景:

  • 假設(shè)我們有一組redis集群,集群中有5個節(jié)點(diǎn)分別是a、b、c、d、e,現(xiàn)在有兩個線程嘗試獲取紅鎖,線程1先到達(dá),成功獲取到a、b、d 3個節(jié)點(diǎn)的鎖,假設(shè)在此期間線程1在使用分布式鎖因?yàn)槌绦騍TW等原因?qū)е孪到y(tǒng)阻塞未能及時續(xù)約,線程2在此時就可以同時獲取到a、b、d3個節(jié)點(diǎn)的分布式鎖,導(dǎo)致鎖互斥失?。?/li>

  • 還是以上述的部署架構(gòu)為例,假設(shè)線程1針對a、b、d上鎖成功,此時a節(jié)點(diǎn)因?yàn)槟承┰驅(qū)r鐘向前調(diào)整了一些,導(dǎo)致a節(jié)點(diǎn)提前超時,線程2基于a、c、d還是會拿到分布式鎖,又一次導(dǎo)致互斥失?。?/li>

同時為了實(shí)現(xiàn)一個分布式互斥問題,提出紅鎖這樣一個復(fù)雜的實(shí)現(xiàn)方案,不僅增加了系統(tǒng)的復(fù)雜度,涉及多個網(wǎng)絡(luò)節(jié)點(diǎn)的通信開銷也導(dǎo)致分布式鎖的執(zhí)行性能下降。

所以總的來說無論是從正確性還是效率,Martin都認(rèn)為紅鎖都達(dá)不到要求,因此也被Redisson廢棄,感興趣的讀者也可以看看Martin的原文:https://news.ycombinator.com/item?id=11059738

責(zé)任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關(guān)推薦

2023-05-12 08:02:43

分布式事務(wù)應(yīng)用

2024-01-02 13:15:00

分布式鎖RedissonRedis

2022-08-04 08:45:50

Redisson分布式鎖工具

2021-07-26 11:09:46

Redis分布式技術(shù)

2024-11-28 15:11:28

2025-07-30 09:34:04

2021-07-06 08:37:29

Redisson分布式

2021-02-28 07:49:28

Zookeeper分布式

2021-11-29 00:18:30

Redis分布式

2024-05-10 08:18:16

分布式數(shù)據(jù)庫

2024-08-15 08:03:52

2021-06-30 14:56:12

Redisson分布式公平鎖

2021-07-02 08:51:09

Redisson分布式鎖公平鎖

2025-04-11 09:57:16

2024-07-29 09:57:47

2021-07-03 17:45:57

分布式Redisson MultiLock

2023-09-04 08:12:16

分布式鎖Springboot

2023-08-27 22:13:59

Redisson分布式緩存

2021-07-01 09:42:08

Redisson分布式

2024-10-09 17:12:34

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

国产探花在线精品| 久草在线新免费首页资源站| 免费在线观看日韩欧美| 视频在线观看一区二区| 国产大片一区二区三区| 国内高清免费在线视频| 97精品超碰一区二区三区| 日韩免费观看视频| 国产一区在线观看免费| 91精品久久久久久综合五月天| 狠狠操狠狠色综合网| 亚洲砖区区免费| 亚洲精品无遮挡| 久久一区视频| 久久影视免费观看| 无码人妻精品一区二区三区温州| 成人精品三级| 婷婷综合久久一区二区三区| 日韩成人av电影在线| 亚洲女人18毛片水真多| 免费在线观看一区二区三区| 97久久久免费福利网址| 黑人狂躁日本娇小| 九九久久成人| 日韩欧美成人激情| 欧美日韩一区二区三区69堂| a级片在线免费观看| 亚洲人123区| 日韩精品无码一区二区三区| 免费国产羞羞网站视频| 久久91精品国产91久久小草| 欧美一级电影久久| 黄色小说在线观看视频| 国产精品99久久精品| 亚洲精品在线不卡| 国产51自产区| 国产95亚洲| 在线精品国精品国产尤物884a| 欧美不卡在线播放| a视频在线观看免费| 亚洲国产精品av| 久久天天狠狠| 蜜臀av午夜精品| 国产麻豆视频精品| 国产一区视频在线| 最近中文字幕免费观看| 久久成人亚洲| 欧美亚洲成人网| 国产精品白浆一区二小说| 久久久久av| 色综合伊人色综合网站| 乐播av一区二区三区| 少妇高潮一区二区三区| 精品国产91久久久久久久妲己 | 国产精选久久久久久| 国产成人精品777777| 一区三区视频| 97国产精品久久| xxxxxx国产| 夜久久久久久| 91精品国产91久久久| 精品久久免费视频| 国产日韩精品视频一区二区三区| 97在线日本国产| 亚洲第一精品在线观看| 一区二区毛片| 555www成人网| 青青草免费观看视频| 日韩午夜在线| 日韩av黄色在线观看| 日韩在线视频不卡| 老牛国产精品一区的观看方式| 国产不卡视频在线| 五月婷婷丁香在线| 精品一区二区免费视频| 91在线视频九色| 亚洲av无码一区二区三区性色| 国产成人aaaa| 精品国产乱码久久久久久88av| 水中色av综合| 国产精品午夜免费| 视频一区二区视频| 七七久久电影网| 午夜精彩视频在线观看不卡| 丰满爆乳一区二区三区| 日韩pacopacomama| 欧美日韩国产精品自在自线| 天堂av.com| 视频小说一区二区| 正在播放欧美一区| 久久无码精品丰满人妻| 久久国产精品久久久久久电车| 国产精品中文字幕在线观看| 国内毛片毛片毛片毛片| 99re热视频这里只精品| 伊人久久大香线蕉av一区| 啦啦啦中文在线观看日本| 91国偷自产一区二区使用方法| 欧美一级特黄aaa| 久久久久久毛片免费看 | 国产日产久久高清欧美一区| 国产高清免费观看| 91看片淫黄大片一级在线观看| 亚洲日本japanese丝袜| 久久www人成免费看片中文| 欧美中文一区二区三区| 性高潮久久久久久| 精品国产一区二区三区四区| 免费av一区二区| 精品国产一区二区三区四| 国产在线播放一区| 蜜桃精品久久久久久久免费影院| 精品黄色免费中文电影在线播放| 午夜精品123| 五月天开心婷婷| 亚州综合一区| 久久91亚洲人成电影网站| 亚洲色成人www永久网站| 国产91露脸合集magnet| 亚洲精品日韩成人| 国产拍在线视频| 欧美高清视频在线高清观看mv色露露十八 | 国产精品果冻传媒| 国产精品成久久久久| 欧美亚洲国产视频小说| 精品久久久久中文慕人妻 | 国产精品久久亚洲| 在线视频91p| 日本一本不卡| 成人高潮a毛片免费观看网站| 日韩精品中文字幕在线| 曰本女人与公拘交酡| 日韩国产精品久久| 久久久久久99| 国产精品探花在线| 欧美一区二区三区免费视频 | 精品国产一区二区三区久久| 无码人妻丰满熟妇精品| www.成人在线| 成人免费毛片在线观看| 国产精品日本一区二区不卡视频| 伊人久久大香线蕉av一区二区| 香蕉影院在线观看| 91在线一区二区| 成人免费观看cn| 精品日产乱码久久久久久仙踪林| 欧美丰满老妇厨房牲生活| 99久久精品国产一区二区成人| 国产精品卡一卡二| 中文字幕第100页| 欧美亚洲国产激情| 国产精品99久久久久久久久| 欧美亚洲日本| 色综合亚洲欧洲| 在线 丝袜 欧美 日韩 制服| 99热这里只有精品8| 国产精品一区二区三区观看| sm在线播放| 精品国产91亚洲一区二区三区婷婷 | 久久久精品久久久| 一区二区三区免费观看视频| 日韩一区在线播放| 亚洲女人在线观看| 欧美日韩在线大尺度| 成人蜜桃视频| 成全电影大全在线观看| 亚洲国产免费av| 亚洲 欧美 日韩 综合| 26uuu国产一区二区三区| 男人天堂1024| 欧美亚洲国产一区| 91在线播放国产| 欧美人与禽猛交乱配| 亚洲精品www久久久| 一级成人黄色片| 国产日产欧美一区二区三区| 17c国产在线| 欧美婷婷在线| 欧美日韩精品一区| 韩日精品一区| 九九久久久久久久久激情| 你懂的网站在线| 在线视频亚洲一区| 国产午夜精品理论片| 粉嫩av一区二区三区在线播放| 成年人看的毛片| 欧洲三级视频| 亚洲伊人一本大道中文字幕| 91超碰在线播放| 一本一道久久a久久精品逆3p| 国产一区二区视频免费观看| 亚洲午夜一区二区| 久久美女免费视频| 国产一区二区三区视频在线播放| 99色这里只有精品| 成人在线免费小视频| 51国偷自产一区二区三区的来源| 久热在线观看视频| 日韩视频免费在线| 手机看片1024国产| 777欧美精品| 日韩 欧美 综合| 国产精品不卡在线观看| 少妇一级淫片免费放播放| 麻豆免费精品视频| 青青草国产精品视频| 欧美日韩国产高清电影| 国产精品国产三级国产专区53| 欧美日韩激情电影| 午夜精品一区二区三区av| 99青草视频在线播放视| 亚洲第一色中文字幕| 91久久精品国产91性色69| 一区二区三区欧美在线观看| japanese中文字幕| 成a人片国产精品| 国产精品久久久久久9999| 午夜亚洲视频| 久久成人福利视频| 国产精品久久久久9999赢消| 欧美日韩在线高清| 国产精伦一区二区三区| 成人国产精品日本在线| 国产精品扒开腿做爽爽爽视频软件| 九九久久综合网站| 成人片在线看| 一本色道久久综合亚洲精品小说 | 亚洲日本成人网| 亚洲黄色在线免费观看| 欧美精品丝袜久久久中文字幕| 天天干,天天干| 婷婷国产v国产偷v亚洲高清| 欧美交换国产一区内射| 亚洲欧美日韩国产成人精品影院| 亚洲色图 激情小说| 久久久久9999亚洲精品| 欲求不满的岳中文字幕| 成人永久免费视频| 污污视频在线免费| 激情综合五月天| 黑森林精品导航| 视频一区欧美精品| 97在线免费公开视频| 一本色道精品久久一区二区三区 | 欧美精品九九| av不卡在线免费观看| 色999国产精品| 亚洲精品一区二| 成人高清av| 日韩欧美在线一区二区| 欧美亚洲在线日韩| 少妇精品久久久久久久久久| 成人免费a**址| 视频一区视频二区视频| japanese国产精品| 色噜噜色狠狠狠狠狠综合色一| 国产日韩欧美一区二区三区| 日产中文字幕在线精品一区| 狠狠色狠狠色综合婷婷tag| 欧美日韩在线精品| 欧美日韩一区二区三区视频播放| 日韩欧美一区二区三区久久婷婷| 日韩欧美1区| 国产奶头好大揉着好爽视频| 综合av在线| 欧美中日韩在线| 国产偷自视频区视频一区二区| 又粗又黑又大的吊av| 美女精品在线| 亚洲国产成人va在线观看麻豆| 国产自产2019最新不卡| wwwww在线观看| 波多野结衣中文字幕一区| 免费看黄色aaaaaa 片| 欧美高清在线视频| 农村老熟妇乱子伦视频| 亚洲日本一区二区| 国产一级性生活| 色综合久久综合| 91免费视频播放| 日韩女优电影在线观看| 天堂网在线中文| 国产亚洲视频在线| 成人av黄色| 97人人模人人爽人人喊中文字| 欧美日韩视频网站| 成人激情在线观看| 嫩草国产精品入口| 日韩国产伦理| 欧美1级日本1级| 黄色片一级视频| 极品销魂美女一区二区三区| 久久久午夜精品福利内容| 国产精品色婷婷| 国产一级在线视频| 欧美在线影院一区二区| 亚洲精品无码久久久| 怡红院精品视频| av人人综合网| 国产精品丝袜白浆摸在线| 国产精品nxnn| 一区二区三区在线观看www| 99精品国产99久久久久久福利| 九色porny91| 高清不卡在线观看| av片在线免费看| 狠狠爱在线视频一区| 精品国产伦一区二区三区| 一区二区av在线| 91超碰在线免费| 91亚洲精华国产精华| 欧美人与物videos另类xxxxx| 亚洲小视频在线播放| 久久精品日产第一区二区 | 久久午夜免费视频| 日韩欧美你懂的| 99青草视频在线播放视| 奇米成人av国产一区二区三区| 亚洲国产高清在线观看| 亚洲第一在线综合在线| 亚洲一区黄色| 精品国产aⅴ一区二区三区东京热 久久久久99人妻一区二区三区 | 欧美日韩国产123| 久久精品国产福利| 欧美精品二区三区四区免费看视频| 一区二区三区网站| 人人干人人干人人| 久久久综合激的五月天| 久久久99精品| 欧美一区二区在线观看| 午夜免费福利在线观看| 国产成人在线亚洲欧美| 秋霞蜜臀av久久电影网免费| 人人妻人人做人人爽| 国产成人欧美日韩在线电影| 日韩在线一卡二卡| 欧美色倩网站大全免费| 秋霞av在线| 欧美一级片一区| 天海翼精品一区二区三区| 国产69精品久久久久999小说| 国产91精品久久久久久久网曝门| 九九精品视频免费| 91精品国产全国免费观看| 日本不卡不卡| 成人欧美一区二区三区黑人孕妇 | 亚洲欧美日韩中文在线| 欧美男人天堂| 美日韩免费视频| 性欧美暴力猛交另类hd| 女人被狂躁c到高潮| 福利微拍一区二区| 免费黄色在线视频网站| 热99精品只有里视频精品| 亚洲欧洲美洲国产香蕉| 超碰影院在线观看| 国产丝袜美腿一区二区三区| 无码人妻熟妇av又粗又大| 中文字幕亚洲专区| 欧美91在线|欧美| 亚洲一区三区视频在线观看| 另类综合日韩欧美亚洲| 中文字幕无码日韩专区免费 | 伊人22222| 久久亚洲综合国产精品99麻豆精品福利| 日韩在线激情| 午夜啪啪福利视频| 国产精品小仙女| 日本一二三区不卡| 亚洲男人的天堂在线| 最新日韩一区| 正在播放亚洲| 成人午夜精品在线| 久久久久女人精品毛片九一| 中文字幕亚洲在线| 亚洲码欧美码一区二区三区| 国产玉足脚交久久欧美| 26uuu久久综合| 中文字幕自拍偷拍| 欧美人与性动交a欧美精品| 精品国产影院| 精品日韩久久久| 亚洲另类色综合网站| 三区在线观看| 成人在线激情视频| 国产日韩一区二区三区在线| 日韩影视一区二区三区| 欧美不卡一区二区| 日本电影欧美片| 日韩亚洲欧美一区二区| 久久在线免费观看| 国产夫妻性生活视频| 日本老师69xxx| 亚洲一级毛片| 黑人巨大精品欧美| 日韩一区二区免费在线电影| 正在播放日韩精品| 欧美 亚洲 视频| 国产三级三级三级精品8ⅰ区| 亚洲精品97久久中文字幕|