精品欧美一区二区三区在线观看 _久久久久国色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ù)棧公眾號

交换做爰国语对白| 国产精品乱码| 精品少妇一区二区三区密爱| 国产精品久久久久久久久久齐齐| 国产精品久久一级| 高清国产一区| 波多野结衣电影在线播放| 国产精品久久久久久影院8一贰佰 国产精品久久久久久麻豆一区软件 | 欧美色图另类小说| 午夜在线小视频| 国产传媒欧美日韩成人| 国产精品白嫩初高中害羞小美女 | 国产精品成久久久久三级| 成年人av电影| 欧美特黄一级大片| 亚洲国产精品久久精品怡红院| 男女污污的视频| av中文在线资源| 《视频一区视频二区| 青青成人在线| 凸凹人妻人人澡人人添| 国产在线精品免费| 国产精品高潮呻吟久久av无限| 久久久精品视频免费观看| 精品国产aⅴ| 亚洲精品国产精品久久清纯直播| 亚洲图片 自拍偷拍| 日韩成人在线资源| 91成年人网站| 女同久久另类99精品国产| 欧美高清视频不卡网| 四虎永久在线精品无码视频| 日本精品600av| 国产精品情趣视频| 欧美日韩免费高清| 五月天激情婷婷| 成人中文字幕在线| 97超碰最新| 国产日本精品视频| 久久99精品国产.久久久久| 国产精品第100页| 五月天激情四射| 国产伦理一区| 欧美在线观看一区二区三区| 国产在线拍揄自揄拍| 中文一区一区三区免费在线观看| 色诱女教师一区二区三区| 青娱乐国产视频| 国产精品欧美在线观看| 国产丝袜精品第一页| 中文字幕 亚洲一区| 成人自拍在线| 日韩免费一区二区| 久久久久亚洲av成人网人人软件| 精品91福利视频| 欧美一区午夜视频在线观看| 福利视频999| 四虎永久精品在线| 91精品国产麻豆| 日本一二三四区视频| 精品成人18| 精品国产露脸精彩对白| 制服丝袜第一页在线观看| 国产精品毛片久久久| 日韩精品在线播放| 国产亚洲精品熟女国产成人| 经典一区二区| 最新日韩中文字幕| 欧美激情图片小说| 国户精品久久久久久久久久久不卡| 亚洲一区二区三区中文字幕| 亚洲成人第一| 久操视频在线播放| 亚洲激情自拍视频| 日本丰满少妇xxxx| 成人片免费看| 欧美日韩精品一区二区三区蜜桃| 蜜桃福利午夜精品一区| 豆花视频一区二区| 亚洲美女视频网| 三级黄色免费观看| 狠狠爱综合网| 日韩av免费看网站| 国产一区二区在线不卡| 成人永久看片免费视频天堂| 久久综合狠狠综合久久综青草| 国产在线观看免费网站| 综合久久久久久| 免费看又黄又无码的网站| 性欧美1819sex性高清| 欧美久久一二区| 中文字幕第3页| 日韩成人免费| 久久久午夜视频| 天天天天天天天干| 成人午夜在线播放| 亚洲一区二区三区欧美| segui88久久综合| 欧美日韩在线播| 黄色国产在线视频| 99精品网站| 韩国福利视频一区| 91精东传媒理伦片在线观看| 北条麻妃一区二区三区| 亚洲视频sss| 国产理论在线| 欧美一区二区视频网站| 欧美一区二区三区成人精品| 亚洲精品国产偷自在线观看| 国产91色在线播放| 亚洲h视频在线观看| 国产午夜亚洲精品不卡| 欧美国产日韩激情| 亚洲福利影视| 亚洲欧美日韩成人| 日本天堂网在线观看| 久久99精品久久久| 日本一区二区视频| 18aaaa精品欧美大片h| 777奇米成人网| 天天躁夜夜躁狠狠是什么心态| 黄色成人在线网址| 91麻豆桃色免费看| av中文资源在线| 欧美性xxxx极品hd满灌| 熟妇高潮一区二区| 欧美一区二区| 国产在线观看91精品一区| 麻豆影视在线| 欧美日韩国产丝袜美女| 国产国语老龄妇女a片| 香蕉国产精品| 成人a视频在线观看| 99视频在线观看地址| 色婷婷狠狠综合| 日韩成人av一区二区| 红桃视频国产精品| 亚洲综合一区二区不卡| 蜜桃视频在线观看www社区 | 992tv快乐视频| 日韩午夜电影免费看| 一区二区三区www| 加勒比在线一区| 国产午夜三级一区二区三| 日韩av一二三四区| 免费av一区二区三区四区| 98视频在线噜噜噜国产| 天天操天天干天天干| 午夜精品视频在线观看| 国产精品久久AV无码| 99精品热6080yy久久| 久久99热只有频精品91密拍| a级片免费在线观看| 亚洲国模精品私拍| 亚洲 欧美 日韩 综合| 99久久综合精品| 日本黄色三级大片| 欧美日韩色图| 成人h片在线播放免费网站| 老司机在线永久免费观看| 69久久夜色精品国产69蝌蚪网| 国产一二三av| 国产精品一二一区| 日韩精品在线中文字幕| 久久精品凹凸全集| 日本精品性网站在线观看| 精品视频二区| 欧美精品自拍偷拍动漫精品| 日本老熟俱乐部h0930| 成人国产视频在线观看| 日韩精品―中文字幕| 视频国产一区| 亚洲qvod图片区电影| 美女尤物在线视频| 亚洲乱码一区av黑人高潮| 青青艹在线观看| 亚洲欧洲国产专区| 国产精品久久久久久亚洲色 | 成人午夜视频一区二区播放| 午夜精品免费在线| 日本猛少妇色xxxxx免费网站| 韩国精品免费视频| av在线播放亚洲| 欧美日韩一区二区综合| 91在线短视频| 国产另类xxxxhd高清| 久热在线中文字幕色999舞| 天天干天天干天天干| 欧美日韩成人高清| 国产一级特黄aaa大片| 欧美—级在线免费片| 中文字幕永久免费| 久久青草久久| 欧美日韩午夜爽爽| 精品国产一区二区三区香蕉沈先生 | 亚洲欧美日韩天堂一区二区| 国产精品久久久久久无人区 | 日韩亚洲一区二区| 日本xxxxxwwwww| 欧美色国产精品| 国产精品成人久久| 国产精品成人免费在线| 亚洲男女在线观看| 久久国产精品一区二区| 国产在线精品91| 欧美在线亚洲| 日本一区免费看| 大奶在线精品| 亚洲综合国产精品| 欧美国产日韩电影| 91精品成人久久| 99在线视频观看| 中文字幕日韩综合av| 日批视频免费播放| 337p亚洲精品色噜噜| 精产国品一区二区| 亚洲动漫第一页| 天天做夜夜爱爱爱| 国产校园另类小说区| avtt香蕉久久| 国v精品久久久网| 樱花草www在线| 免费观看久久久4p| 免费日韩中文字幕| 99re国产精品| 男人天堂av片| 欧美久久一级| 手机在线观看国产精品| 中文精品一区二区| 精品国产乱码久久久久| 亚洲综合影院| 超碰97在线资源| 免费观看性欧美大片无片| 国产一区私人高清影院| 欧美成人一二区| 国产精品一区二区久久国产| 欧美男女交配| 国产不卡一区二区在线播放| 欧美三级网站| 欧美亚洲成人免费| 亚洲电影观看| 日本伊人精品一区二区三区介绍| 三妻四妾的电影电视剧在线观看| 久久久久久久久久国产精品| 免费电影视频在线看| 久操成人在线视频| 免费毛片在线看片免费丝瓜视频| 久久久久久久爱| 俄罗斯一级**毛片在线播放| 久久久久久网站| 国产高清在线a视频大全 | jizz亚洲女人高潮大叫| 国产精品美女www| 亚洲视频资源| 91久久大香伊蕉在人线| www.爱久久| 久久99精品久久久久久水蜜桃| 欧美一级三级| 欧美日韩最好看的视频| 国产一区二区精品福利地址| 水蜜桃一区二区三区| 久久精品国内一区二区三区水蜜桃 | 深夜影院在线观看| 亚洲免费视频一区二区| 春暖花开成人亚洲区| 少妇av一区二区三区| eeuss影院www在线观看| 日韩一中文字幕| 羞羞视频在线免费国产| 久久久久久久久亚洲| 亚洲天堂av影院| 国产精品亚洲欧美导航| 久久99成人| 精品久久精品久久| 激情综合网五月| 免费的一级黄色片| 一区二区三区国产在线| 三级a在线观看| 国产精品一区二区不卡| 亚洲国产果冻传媒av在线观看| 久久女同精品一区二区| 久久av红桃一区二区禁漫| 亚洲地区一二三色| 高潮毛片又色又爽免费 | 欧美一级精品| 91传媒免费视频| 午夜在线一区二区| 91丝袜超薄交口足| 99久久久免费精品国产一区二区| www.99热| 五月天网站亚洲| 在线播放成人av| 亚洲精品一区二区在线观看| 国产在线视频福利| 欧美激情免费看| 成人黄色免费观看| 精品久久久久久乱码天堂| 午夜精品一区二区三区国产 | 中文字幕日韩精品一区二区| 影音先锋中文字幕一区| 亚欧美在线观看| 97精品国产97久久久久久久久久久久| 国产白丝一区二区三区 | 久久免费在线观看| 99久久精品一区二区成人| 国产另类自拍| 亚洲欧美偷拍自拍| 亚洲成色www.777999| 国产成人免费在线视频| 中文字幕第二区| 欧美性猛交xxxx乱大交3| www.激情五月| 中文字幕在线看视频国产欧美在线看完整 | 久久久这里只有精品视频| 国产91精品在线| 日本免费高清一区二区| 在线视频观看日韩| 青娱乐国产精品视频| 中文幕一区二区三区久久蜜桃| 天天做天天爱夜夜爽| 欧美v国产在线一区二区三区| 香蕉视频网站在线观看| 国产成人综合一区二区三区| 色狠狠久久av综合| 日韩精品在线中文字幕| 国产一区二区在线观看视频| 国产欧美一区二区三区在线观看视频| 欧美性猛交xxxx免费看漫画 | 91蜜桃免费观看视频| 免费一级a毛片夜夜看| 9191成人精品久久| 日本在线观看网站| 国产精品久久久精品| 欧美美乳视频| 免费在线观看毛片网站| 久久久久久久久久久久久久久99 | 日韩精品一区二区三区中文不卡 | 一区二区三区欧美亚洲| 国产三区在线播放| 美女少妇精品视频| 日韩欧美中文字幕一区二区三区| 中文有码久久| 国产主播一区二区三区| 香蕉久久久久久久| 欧美精品久久99久久在免费线 | 超碰超碰在线观看| 中文字幕精品—区二区四季| 成年人晚上看的视频| 亚洲一级免费视频| 国产成人精品一区二三区在线观看 | 欧美女v视频| 国产99久久精品一区二区 夜夜躁日日躁 | 国产婷婷成人久久av免费高清| 国产高潮在线| 欧美二区三区| 日本成人中文字幕在线视频| 超薄肉色丝袜一二三| 欧美日韩国产精品自在自线| 国产理论在线观看| 亚洲精品欧美日韩| 伊人久久成人| 久久久久久久久久久久| 欧美日韩亚洲综合| 永久免费网站在线| 精品一区二区视频| 日韩制服丝袜av| 最新av电影网站| 欧美精品一区二区三区蜜桃| 欧美gv在线| 亚洲一区三区在线观看| 国产黄人亚洲片| 国产三级av片| 日韩亚洲综合在线| 国产福利资源一区| 无码少妇一区二区三区芒果| 国产精品高潮呻吟久久| 性生活免费网站| 欧美最猛性xxxxx免费| 91综合久久一区二区| 日本美女视频网站| 在线观看欧美黄色| 青青草原av在线| 日韩成人av电影在线| 国产成人免费在线视频| 亚洲乱码国产乱码精品| 久久精品最新地址| 欧美性生活一级片| 手机免费av片| 午夜精品久久久久久| 在线国产情侣| 精品不卡在线| 国产麻豆91精品| 午夜婷婷在线观看| 精品视频9999| 精品国产美女| 91黄色免费视频| 91精品福利在线一区二区三区 | 男女全黄做爰文章| 日韩电影网在线| 91丨精品丨国产|