最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于setnx,lua腳本和Redisson詳解Redis分布式鎖實(shí)現(xiàn)的三種方式

 更新時(shí)間:2026年03月02日 10:02:27   作者:造輪子的豬  
分布式鎖是解決分布式系統(tǒng)中多節(jié)點(diǎn)并發(fā)訪問共享資源的核心方案,本文從原理層面拆解Redis分布式鎖的核心邏輯,并詳細(xì)分析三種常見實(shí)現(xiàn)方式的代碼邏輯、優(yōu)缺點(diǎn)及生產(chǎn)環(huán)境注意事項(xiàng),感興趣的朋友跟隨小編一起看看吧

分布式鎖是解決分布式系統(tǒng)中多節(jié)點(diǎn)并發(fā)訪問共享資源的核心方案,Redis憑借高性能、原子性操作等特性,成為實(shí)現(xiàn)分布式鎖的主流選擇。本文從原理層面拆解Redis分布式鎖的核心邏輯,并詳細(xì)分析三種常見實(shí)現(xiàn)方式的代碼邏輯、優(yōu)缺點(diǎn)及生產(chǎn)環(huán)境注意事項(xiàng)。

一、Redis分布式鎖核心原理

1.1 核心設(shè)計(jì)目標(biāo)

一個(gè)可靠的分布式鎖需滿足以下特性:

  • 互斥性:同一時(shí)刻只能有一個(gè)客戶端持有鎖,避免并發(fā)操作共享資源;
  • 安全性:鎖只能由持有者釋放,不能被其他客戶端誤刪;
  • 超時(shí)釋放:避免客戶端持有鎖后宕機(jī),導(dǎo)致鎖永久無法釋放(死鎖);
  • 原子性:加鎖、釋放鎖的核心操作需原子執(zhí)行,避免并發(fā)場景下的邏輯漏洞;
  • 可重入(可選):同一客戶端持有鎖后,再次請求鎖時(shí)無需重新獲?。ㄔ鰪?qiáng)易用性)。

1.2 Redis實(shí)現(xiàn)鎖的核心基礎(chǔ)

Redis通過以下核心命令支撐分布式鎖實(shí)現(xiàn):

命令/特性作用
SET key value NX EX t原子執(zhí)行“不存在則設(shè)置(NX)+ 過期時(shí)間(EX)”,避免加鎖與設(shè)超時(shí)的拆分操作
DEL key刪除鎖(釋放鎖),需配合校驗(yàn)鎖歸屬,避免誤刪
Lua腳本將“校驗(yàn)鎖歸屬+釋放鎖”封裝為原子操作,解決釋放鎖的并發(fā)安全問題
Redisson(客戶端)基于Redis封裝了可重入、自動續(xù)期、公平鎖等高級特性,簡化鎖的使用

二、三種實(shí)現(xiàn)方式詳解(邏輯+問題分析)

方式1:基礎(chǔ)實(shí)現(xiàn)(SetNX + 手動校驗(yàn)釋放)

2.1 代碼邏輯拆解

@Resource
private StringRedisTemplate stringRedisTemplate;
/**
 * 示例:扣減庫存(基礎(chǔ)分布式鎖實(shí)現(xiàn))
 */
private void order(){
    // 1. 生成唯一鎖值(用于校驗(yàn)鎖歸屬,避免誤刪)
    String lockValue = UUID.randomUUID().toString();
    // 2. 加鎖:SETNX + 過期時(shí)間(原子操作),30秒自動釋放
    Boolean locked = stringRedisTemplate.opsForValue()
            .setIfAbsent("product:1001:lock", lockValue, 30, TimeUnit.SECONDS);
    try {
        // 3. 加鎖成功則執(zhí)行業(yè)務(wù)邏輯(扣減庫存)
        if (locked) {
            Integer count = (Integer) stringRedisTemplate.opsForHash().get("product:1001","number");
            if (count > 0) {
                stringRedisTemplate.opsForHash().put("product:1001", "number", count - 1);
            }
        }
    } finally {
        // 4. 釋放鎖:先校驗(yàn)鎖歸屬,再刪除(非原子操作)
        if (lockValue.equals(stringRedisTemplate.opsForValue().get("product:1001:lock"))) {
            stringRedisTemplate.delete("product:1001:lock");
        }
    }
}

2.2 核心邏輯

  1. 加鎖:通過setIfAbsent(底層是SET NX EX)實(shí)現(xiàn)原子加鎖,同時(shí)設(shè)置30秒超時(shí),避免死鎖;
  2. 鎖歸屬校驗(yàn):用UUID生成唯一lockValue,釋放鎖前校驗(yàn)值是否匹配,防止誤刪其他客戶端的鎖;
  3. 釋放鎖:finally塊中執(zhí)行釋放邏輯,確保業(yè)務(wù)執(zhí)行完(或異常)后釋放鎖。

2.3 存在的核心問題

  • 釋放鎖非原子性:“校驗(yàn)鎖歸屬 + 刪除鎖”是兩步操作,若校驗(yàn)后鎖恰好過期,此時(shí)其他客戶端已加鎖,當(dāng)前客戶端執(zhí)行delete會誤刪新鎖;
  • 無重試機(jī)制:加鎖失敗直接放棄,實(shí)際場景中需結(jié)合業(yè)務(wù)設(shè)置重試邏輯(如循環(huán)重試+休眠);
  • 無鎖續(xù)期:若業(yè)務(wù)執(zhí)行時(shí)間超過30秒,鎖會自動過期,導(dǎo)致多個(gè)客戶端同時(shí)執(zhí)行業(yè)務(wù),破壞互斥性;
  • Hash操作類型轉(zhuǎn)換風(fēng)險(xiǎn)stringRedisTemplate.opsForHash().get()返回Object,強(qiáng)轉(zhuǎn)Integer可能出現(xiàn)類型異常(需先判空+類型校驗(yàn))。

方式2:優(yōu)化版(Lua腳本保證釋放鎖原子性)

2.1 代碼邏輯拆解

@Resource
private StringRedisTemplate stringRedisTemplate;
private static final String LOCK_KEY = "product:1001:lock";
private static final String STOCK_KEY = "product:1001:number";
private static final long LOCK_TIMEOUT = 30; // 鎖超時(shí)時(shí)間(秒)
private static final long SLEEP_TIME = 100; // 重試間隔(毫秒)
private void order() {
    String lockValue = UUID.randomUUID().toString();
    try {
        // 1. 嘗試獲取鎖(原子加鎖)
        Boolean locked = tryAcquireLock(lockValue);
        if (!locked) {
            // 加鎖失敗可重試/返回失?。ㄊ纠苯臃祷兀瑢?shí)際可加循環(huán)重試)
            return;
        }
        // 2. 執(zhí)行業(yè)務(wù):獲取并扣減庫存(簡化為String結(jié)構(gòu),避免Hash類型轉(zhuǎn)換問題)
        String stockStr = stringRedisTemplate.opsForValue().get(STOCK_KEY);
        if (stockStr == null || Integer.parseInt(stockStr) <= 0) {
            return;
        }
        stringRedisTemplate.opsForValue().set(STOCK_KEY, String.valueOf(Integer.parseInt(stockStr) - 1));
    } finally {
        // 3. 釋放鎖:Lua腳本封裝“校驗(yàn)+刪除”,保證原子性
        releaseLock(lockValue);
    }
}
/**
 * 原子加鎖:SET NX EX
 */
private Boolean tryAcquireLock(String lockValue) {
    return stringRedisTemplate.opsForValue()
            .setIfAbsent(LOCK_KEY, lockValue, LOCK_TIMEOUT, TimeUnit.SECONDS);
}
/**
 * 原子釋放鎖:Lua腳本
 */
private void releaseLock(String lockValue) {
    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
            "return redis.call('del', KEYS[1]) " +
            "else " +
            "return 0 " +
            "end";
    stringRedisTemplate.execute(
            new DefaultRedisScript<>(script, Long.class),
            Arrays.asList(LOCK_KEY),
            lockValue
    );
}

2.2 核心優(yōu)化點(diǎn)

  1. 釋放鎖原子化:將“校驗(yàn)鎖歸屬(get)+ 刪除鎖(del)”封裝為Lua腳本,Redis會原子執(zhí)行腳本內(nèi)容,徹底解決方式1的“誤刪鎖”問題;
  2. 簡化庫存存儲:將庫存從Hash改為String結(jié)構(gòu),避免類型轉(zhuǎn)換異常,降低業(yè)務(wù)復(fù)雜度;
  3. 代碼分層:抽離tryAcquireLockreleaseLock方法,提升代碼復(fù)用性。

2.3 仍存在的問題

  • 無鎖續(xù)期:核心問題未解決!若業(yè)務(wù)執(zhí)行時(shí)間(如扣減庫存需40秒)超過LOCK_TIMEOUT(30秒),鎖會提前過期,導(dǎo)致并發(fā)安全問題;
  • 重試邏輯缺失:示例中加鎖失敗直接返回,實(shí)際場景需增加“循環(huán)重試+最大重試次數(shù)”,避免因瞬時(shí)并發(fā)導(dǎo)致加鎖失??;
  • 無異常處理Integer.parseInt(stockStr)未做異常捕獲,若庫存值非數(shù)字會拋出運(yùn)行時(shí)異常;
  • 單點(diǎn)風(fēng)險(xiǎn):依賴單個(gè)Redis節(jié)點(diǎn),若節(jié)點(diǎn)宕機(jī),鎖數(shù)據(jù)丟失,可能導(dǎo)致多個(gè)客戶端同時(shí)加鎖。

方式3:生產(chǎn)級實(shí)現(xiàn)(Redisson客戶端)

Redisson是Redis官方推薦的Java客戶端,內(nèi)置了分布式鎖的完整實(shí)現(xiàn),解決了手動實(shí)現(xiàn)的諸多痛點(diǎn)。

2.1 代碼邏輯拆解

@Resource
private RedissonClient redissonClient;
@Resource
private StringRedisTemplate stringRedisTemplate;
private void order() {
    // 1. 獲取分布式鎖對象(可重入鎖)
    RLock lock = redissonClient.getLock("product:1001:lock");
    try {
        // 2. 加鎖:最多等待10秒,鎖30秒后自動釋放;獲取鎖成功則執(zhí)行業(yè)務(wù)
        if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
            try {
                // 3. 扣減庫存業(yè)務(wù)邏輯
                Integer count = (Integer) stringRedisTemplate.opsForHash().get("product:1001","number");
                if (count != null && count > 0) {
                    stringRedisTemplate.opsForHash().put("product:1001", "number", count - 1);
                }
            } finally {
                // 4. 手動釋放鎖(若業(yè)務(wù)執(zhí)行完未超時(shí),主動釋放)
                lock.unlock();
            }
        }
    } catch (InterruptedException e) {
        // 5. 中斷異常處理,恢復(fù)線程中斷狀態(tài)
        Thread.currentThread().interrupt();
    }
}

2.2 核心優(yōu)勢(Redisson解決的痛點(diǎn))

  1. 自動鎖續(xù)期(看門狗機(jī)制)
    • 若業(yè)務(wù)執(zhí)行時(shí)間超過鎖超時(shí)時(shí)間,Redisson會啟動后臺線程(默認(rèn)每10秒)自動將鎖超時(shí)時(shí)間續(xù)期至30秒;
    • 只有當(dāng)客戶端正常釋放鎖或宕機(jī)時(shí),續(xù)期才會停止,徹底解決“鎖提前過期”問題。
  2. 可重入性:基于Redis的Hash結(jié)構(gòu)存儲鎖的持有次數(shù),同一客戶端多次tryLock不會導(dǎo)致死鎖;
  3. 優(yōu)雅的重試與等待tryLock(waitTime, leaseTime, unit)支持“最大等待時(shí)間”,加鎖失敗時(shí)會阻塞等待,直到超時(shí)或獲取到鎖;
  4. 原子性加鎖/釋放鎖:底層封裝了Lua腳本,保證加鎖、釋放鎖的原子性;
  5. 集群適配:支持Redis主從、哨兵、集群模式,解決單點(diǎn)風(fēng)險(xiǎn)(需配置Redisson的集群模式)。

2.3 需注意的細(xì)節(jié)

  • 解鎖時(shí)機(jī):必須在finally塊中執(zhí)行unlock(),但需先判斷lock.isHeldByCurrentThread(),避免未持有鎖時(shí)執(zhí)行解鎖拋出異常;
  • 異常處理tryLock會拋出InterruptedException,需捕獲并恢復(fù)線程中斷狀態(tài),避免線程狀態(tài)異常;
  • Redisson配置:生產(chǎn)環(huán)境需正確配置RedissonClient(如連接池、超時(shí)時(shí)間、集群節(jié)點(diǎn)),否則會導(dǎo)致鎖性能下降或失效;
  • 鎖粒度:避免使用過大的鎖粒度(如“product:lock”),應(yīng)細(xì)化到具體資源(如“product:1001:lock”),減少鎖競爭。

三、三種實(shí)現(xiàn)方式對比與生產(chǎn)建議

實(shí)現(xiàn)方式優(yōu)點(diǎn)缺點(diǎn)適用場景
方式1(基礎(chǔ)版)代碼簡單、無額外依賴釋放鎖非原子、無續(xù)期、易誤刪鎖測試環(huán)境、低并發(fā)非核心業(yè)務(wù)
方式2(Lua版)釋放鎖原子化、代碼結(jié)構(gòu)清晰無續(xù)期、重試邏輯需手動實(shí)現(xiàn)、單點(diǎn)風(fēng)險(xiǎn)中小并發(fā)、核心邏輯簡單場景
方式3(Redisson)自動續(xù)期、可重入、集群適配引入Redisson依賴、配置稍復(fù)雜生產(chǎn)環(huán)境、高并發(fā)核心業(yè)務(wù)

生產(chǎn)環(huán)境核心建議

  1. 優(yōu)先使用Redisson:手動實(shí)現(xiàn)分布式鎖易遺漏邊界條件(如續(xù)期、原子性、集群),Redisson封裝了成熟的解決方案,是生產(chǎn)首選;
  2. 鎖超時(shí)時(shí)間合理設(shè)置:結(jié)合業(yè)務(wù)平均執(zhí)行時(shí)間設(shè)置(如業(yè)務(wù)平均執(zhí)行5秒,設(shè)置超時(shí)30秒),避免過短導(dǎo)致續(xù)期頻繁,過長導(dǎo)致死鎖風(fēng)險(xiǎn);
  3. 避免長時(shí)間持有鎖:分布式鎖應(yīng)“快進(jìn)快出”,執(zhí)行業(yè)務(wù)邏輯時(shí)避免耗時(shí)操作(如數(shù)據(jù)庫慢查詢、遠(yuǎn)程調(diào)用),必要時(shí)拆分鎖粒度;
  4. 集群模式適配:若Redis為集群/哨兵模式,Redisson需配置RedissonNodeClusterServersConfig,避免主從切換導(dǎo)致鎖丟失;
  5. 兜底方案:分布式鎖失效時(shí),需有兜底邏輯(如數(shù)據(jù)庫樂觀鎖),避免數(shù)據(jù)一致性問題。

四、總結(jié)

Redis分布式鎖的核心是原子加鎖+安全釋放+超時(shí)兜底

  1. 基礎(chǔ)實(shí)現(xiàn)(方式1)僅適用于測試,核心問題是釋放鎖非原子、無續(xù)期;
  2. Lua腳本優(yōu)化版(方式2)解決了釋放鎖原子性問題,但仍需手動處理續(xù)期、重試等邏輯;
  3. Redisson(方式3)是生產(chǎn)級方案,通過看門狗機(jī)制、可重入性、集群適配,解決了手動實(shí)現(xiàn)的所有核心痛點(diǎn)。

生產(chǎn)環(huán)境中,除非有特殊定制需求,否則優(yōu)先基于Redisson實(shí)現(xiàn)分布式鎖,既保證可靠性,又降低開發(fā)和維護(hù)成本。

到此這篇關(guān)于Redis分布式鎖實(shí)現(xiàn)的三種方式-基于setnx,lua腳本和Redisson的文章就介紹到這了,更多相關(guān)redis分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • redis實(shí)現(xiàn)的四種常見限流策略

    redis實(shí)現(xiàn)的四種常見限流策略

    因?yàn)樵诰W(wǎng)站運(yùn)行期間可能會因?yàn)橥蝗坏脑L問量導(dǎo)致業(yè)務(wù)異常、也有可能遭受別人惡意攻,所以我們對網(wǎng)站要進(jìn)行限流,本文主要介紹了redis四種常見限流策略,感興趣的可以了解一下
    2021-06-06
  • Redis中跳表的實(shí)現(xiàn)原理分析

    Redis中跳表的實(shí)現(xiàn)原理分析

    Redis中的跳表是一種高效的多層鏈表結(jié)構(gòu),通過隨機(jī)概率算法決定節(jié)點(diǎn)的層數(shù),從而實(shí)現(xiàn)快速的插入、刪除和查詢操作,跳表的平均時(shí)間復(fù)雜度為O(logn),最差情況為O(n),每個(gè)節(jié)點(diǎn)包含值和指向更高層節(jié)點(diǎn)的指針,以及回退指針以提高操作效率
    2025-02-02
  • 詳解Redis SCAN命令實(shí)現(xiàn)有限保證的原理

    詳解Redis SCAN命令實(shí)現(xiàn)有限保證的原理

    這篇文章主要介紹了Redis SCAN命令實(shí)現(xiàn)有限保證的原理,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下
    2019-07-07
  • Redis?常見緩存問題總結(jié)

    Redis?常見緩存問題總結(jié)

    這篇文章主要給大家總結(jié)了一些Redis?常見緩存問題,并介紹了解決辦法,文中的圖文示例介紹的非常仔細(xì),感興趣的同學(xué)可以參考閱讀下
    2023-06-06
  • Redis 有序集合的使用場景

    Redis 有序集合的使用場景

    在Redis的學(xué)習(xí)中,有序集合是一種非常實(shí)用的數(shù)據(jù)結(jié)構(gòu),本文就來介紹一下Redis 有序集合的使用場景,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • Redis連接與查詢的實(shí)用操作指南

    Redis連接與查詢的實(shí)用操作指南

    當(dāng)我們需要深入排查緩存問題或驗(yàn)證數(shù)據(jù)一致性時(shí),直接使用 redis-cli 是最有效的手段,但在此之前,你需要知道服務(wù)在哪、如何認(rèn)證以及使用哪些命令,本文精簡了冗余理論,直接提供可落地的操作步驟,跟著步驟走,快速解決你的 Redis 連接與查詢難題,需要的朋友可以參考下
    2026-03-03
  • Redis+AOP+自定義注解實(shí)現(xiàn)限流

    Redis+AOP+自定義注解實(shí)現(xiàn)限流

    這篇文章主要為大家詳細(xì)介紹了如何利用Redis+AOP+自定義注解實(shí)現(xiàn)個(gè)小功能:自定義攔截器限制訪問次數(shù),也就是限流,感興趣的可以了解一下
    2022-06-06
  • 深入理解Redis被覆寫后的失效時(shí)間

    深入理解Redis被覆寫后的失效時(shí)間

    Redis覆寫已存在的鍵會導(dǎo)致其舊的失效時(shí)間被新的鍵值對所取代,本文詳細(xì)解析了在鍵被覆寫時(shí),其失效時(shí)間的變化,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-09-09
  • 淺談Redis緩存擊穿、緩存穿透、緩存雪崩的解決方案

    淺談Redis緩存擊穿、緩存穿透、緩存雪崩的解決方案

    這篇文章主要介紹了淺談Redis緩存擊穿、緩存穿透、緩存雪崩的解決方案,緩存是分布式系統(tǒng)中的重要組件,主要解決在高并發(fā)、大數(shù)據(jù)場景下,熱點(diǎn)數(shù)據(jù)訪問的性能問題,需要的朋友可以參考下
    2023-03-03
  • Redis中BigKey與MoreKey優(yōu)化詳解

    Redis中BigKey與MoreKey優(yōu)化詳解

    這篇文章主要介紹了Redis中BigKey與MoreKey優(yōu)化,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2026-03-03

最新評論

清徐县| 札达县| 新营市| 云浮市| 洪雅县| 西华县| 四平市| 延边| 万荣县| 阿合奇县| 镇原县| 京山县| 沈阳市| 通州区| 塔河县| 石门县| 阳曲县| 家居| 栾川县| 承德县| 巩义市| 永善县| 微博| 新蔡县| 休宁县| 保康县| 镇赉县| 灵台县| 乌兰浩特市| 昂仁县| 称多县| 墨竹工卡县| 桂阳县| 鄱阳县| 临泉县| 河南省| 安西县| 杭州市| 余姚市| 巧家县| 民和|