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

SpringBoot項(xiàng)目中實(shí)現(xiàn)Redis分布式鎖的方法與最佳實(shí)踐

 更新時(shí)間:2025年09月22日 09:49:34   作者:IT橘子皮  
在 Spring Boot 項(xiàng)目中實(shí)現(xiàn) Redis 分布式鎖,主要有手動(dòng)實(shí)現(xiàn)和使用 ?Redisson? 兩種主流方式,下面我來為你詳細(xì)解釋這兩種方式的原理、實(shí)現(xiàn)方法和最佳實(shí)踐,需要的朋友可以參考下

引言

在 Spring Boot 項(xiàng)目中實(shí)現(xiàn) Redis 分布式鎖,主要有手動(dòng)實(shí)現(xiàn)和使用 ?Redisson? 兩種主流方式。下面我來為你詳細(xì)解釋這兩種方式的原理、實(shí)現(xiàn)方法和最佳實(shí)踐。

特性維度手動(dòng)實(shí)現(xiàn) (RedisTemplate)Redisson (推薦)
?實(shí)現(xiàn)復(fù)雜度?較高,需處理所有細(xì)節(jié)低,開箱即用,API 簡潔
?可重入性?需自行實(shí)現(xiàn)?原生支持?
?鎖續(xù)期(Watchdog)??需自行實(shí)現(xiàn)?原生支持?
?超時(shí)與等待?需自行實(shí)現(xiàn)?原生支持多種加鎖策略
?公平鎖/讀寫鎖?不支持?支持?
?集群支持(RedLock)??實(shí)現(xiàn)復(fù)雜?原生支持?
?可靠性?一般,需自行規(guī)避各種邊界問題?,久經(jīng)考驗(yàn)

手動(dòng)實(shí)現(xiàn)基于 RedisTemplate

這種方式讓你更接近底層原理,但需要自己處理鎖續(xù)期、可重入等復(fù)雜問題。

?添加依賴?

pom.xml中添加 Spring Data Redis 依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

?配置 Redis 連接?

application.yml中配置:

spring:
  redis:
    host: localhost
    port: 6379
    # password: yourpassword # 如果設(shè)置了密碼

?分布式鎖工具類?

核心是使用 SET key value NX EX seconds命令原子性地獲取鎖,并用 Lua 腳本保證釋放鎖的原子性(判斷值再刪除)。

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisDistributedLock {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final String LOCK_PREFIX = "lock:";

    /**
     * 嘗試獲取分布式鎖
     * @param lockKey 鎖的業(yè)務(wù)Key,如 "order:123"
     * @param requestId 請(qǐng)求標(biāo)識(shí)(可用UUID),用于安全釋放鎖
     * @param expireTime 鎖的過期時(shí)間(毫秒)
     * @param waitTime 獲取鎖的最大等待時(shí)間(毫秒)
     * @return 是否獲取成功
     */
    public boolean tryLock(String lockKey, String requestId, long expireTime, long waitTime) throws InterruptedException {
        String fullKey = LOCK_PREFIX + lockKey;
        long end = System.currentTimeMillis() + waitTime;

        while (System.currentTimeMillis() < end) { // 在等待時(shí)間內(nèi)循環(huán)嘗試
            // 使用 SET NX EX 命令原子性地嘗試獲取鎖
            Boolean success = redisTemplate.opsForValue()
                    .setIfAbsent(fullKey, requestId, expireTime, TimeUnit.MILLISECONDS);
            if (Boolean.TRUE.equals(success)) {
                return true; // 獲取鎖成功
            }
            Thread.sleep(50); // 短暫休眠,避免活鎖和CPU空轉(zhuǎn)
        }
        return false; // 超時(shí)仍未獲取到鎖
    }

    /**
     * 釋放分布式鎖 (使用Lua腳本保證原子性)
     * @param lockKey 鎖的業(yè)務(wù)Key
     * @param requestId 請(qǐng)求標(biāo)識(shí),必須與加鎖時(shí)一致
     * @return 是否釋放成功
     */
    public boolean unlock(String lockKey, String requestId) {
        String fullKey = LOCK_PREFIX + lockKey;
        // Lua腳本:如果鍵存在且值與預(yù)期相符,則刪除它
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        Long result = redisTemplate.execute(redisScript, Collections.singletonList(fullKey), requestId);
        return result != null && result == 1;
    }
}

?關(guān)鍵點(diǎn):??

  • ?NX (Not eXists)??:確保只有鎖不存在時(shí)才能設(shè)置成功,實(shí)現(xiàn)互斥。
  • ?EX (Expire)??:設(shè)置過期時(shí)間,?這是防止死鎖的關(guān)鍵。即使客戶端崩潰,鎖也會(huì)自動(dòng)釋放。
  • ?唯一值 (requestId)??:使用唯一值(如UUID)標(biāo)識(shí)鎖的持有者,確保只能由加鎖的客戶端來解鎖,?防止誤釋鎖
  • ?Lua 腳本?:釋放鎖時(shí),判斷值和刪除必須是原子操作,否則可能誤刪其他客戶端的鎖。

?在業(yè)務(wù)中使用?

@Service
public class OrderService {
    @Autowired
    private RedisDistributedLock redisLock;

    public void createOrder(String orderId) {
        String lockKey = "order_create:" + orderId;
        String requestId = UUID.randomUUID().toString(); // 生成唯一請(qǐng)求ID

        boolean locked = false;
        try {
            // 嘗試獲取鎖,等待5秒,鎖過期時(shí)間10秒
            locked = redisLock.tryLock(lockKey, requestId, 10000, 5000);
            if (locked) {
                // 成功獲取鎖,執(zhí)行核心業(yè)務(wù)邏輯
                System.out.println("成功獲取鎖,執(zhí)行訂單創(chuàng)建邏輯...");
                // ... (你的業(yè)務(wù)代碼)
            } else {
                // 獲取鎖失敗,處理異常或重試
                throw new RuntimeException("系統(tǒng)繁忙,請(qǐng)稍后再試");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("獲取鎖被中斷", e);
        } finally {
            // 釋放鎖,務(wù)必放在finally塊中確保執(zhí)行
            if (locked) {
                redisLock.unlock(lockKey, requestId);
            }
        }
    }
}

使用 Redisson(推薦)

Redisson 是一個(gè)強(qiáng)大的 Redis Java 客戶端,它提供了直接可用的分布式鎖實(shí)現(xiàn),解決了手動(dòng)實(shí)現(xiàn)的所有痛點(diǎn),如可重入鎖、鎖續(xù)期、等待機(jī)制等。

?添加依賴?

pom.xml中添加 Redisson 依賴:

<dependency>
    <groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.27.0</version> <!-- 請(qǐng)使用最新穩(wěn)定版本 -->
</dependency>

?基本配置 (application.yml)??

spring:
  redis:
    host: localhost
    port: 6379
# Redisson 通常會(huì)自動(dòng)使用Spring Redis的配置

?使用 Redisson 的 RLock?

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;

@Service
public class OrderServiceRedisson {

    @Autowired
    private RedissonClient redissonClient; // 直接注入RedissonClient

    public void createOrder(String orderId) {
        String lockKey = "order:lock:" + orderId;
        RLock lock = redissonClient.getLock(lockKey); // 獲取RLock對(duì)象

        try {
            // 嘗試加鎖,最多等待5秒,鎖持有時(shí)間10秒后自動(dòng)解鎖
            // boolean isLocked = lock.tryLock(5, 10, TimeUnit.SECONDS);

            // 更常用的方式是設(shè)置等待時(shí)間,但讓看門狗自動(dòng)續(xù)期(lockTime傳入-1或使用無參lock())
            boolean isLocked = lock.tryLock(5, TimeUnit.SECONDS);
            if (isLocked) {
                try {
                    // 成功獲取鎖,執(zhí)行業(yè)務(wù)邏輯
                    System.out.println("Redisson鎖獲取成功,執(zhí)行邏輯...");
                    // ... (你的業(yè)務(wù)邏輯,看門狗會(huì)為沒有顯式設(shè)置leaseTime的鎖自動(dòng)續(xù)期)
                } finally {
                    // 釋放鎖
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            } else {
                throw new RuntimeException("系統(tǒng)繁忙,請(qǐng)稍后再試");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("獲取鎖被中斷", e);
        }
    }
}

?Redisson 核心優(yōu)勢(shì):??

  • ?看門狗機(jī)制 (Watchdog)??:如果你沒有顯式指定 leaseTime(鎖持有時(shí)間),Redisson 會(huì)啟動(dòng)一個(gè)看門狗線程,在業(yè)務(wù)執(zhí)行期間自動(dòng)定期續(xù)期鎖,防止業(yè)務(wù)執(zhí)行時(shí)間超時(shí)導(dǎo)致鎖意外釋放。這是手動(dòng)實(shí)現(xiàn)非常容易忽略且難以正確實(shí)現(xiàn)的一點(diǎn)。
  • ?可重入?:同一個(gè)線程可以多次獲取同一把鎖。
  • ?豐富的鎖類型?:支持可重入鎖、公平鎖、聯(lián)鎖、紅鎖(RedLock)、讀寫鎖等。

注意事項(xiàng)與最佳實(shí)踐

?鎖的粒度?:鎖的 Key 要精確,例如 "order:123""order"更好,減少競爭,提升并發(fā)度。

?超時(shí)時(shí)間?:手動(dòng)實(shí)現(xiàn)時(shí),過期時(shí)間不宜過短或過長。過短可能導(dǎo)致業(yè)務(wù)未完成鎖就釋放;過長則故障恢復(fù)慢。Redisson 的看門狗機(jī)制能很好地解決這個(gè)問題。

?務(wù)必釋放鎖?:釋放鎖必須放在 finally代碼塊中執(zhí)行,確保即使業(yè)務(wù)異常也能釋放鎖。

?Redis 模式?:

  • 單機(jī)/主從模式:上述方法適用,但主從異步復(fù)制下,主節(jié)點(diǎn)宕機(jī)可能導(dǎo)致鎖失效(從節(jié)點(diǎn)可能無鎖數(shù)據(jù))。
  • ?紅鎖 (RedLock)??:為解決上述問題,可在多個(gè)獨(dú)立的 Redis 主節(jié)點(diǎn)上嘗試獲取鎖,超過半數(shù)成功才算真正獲取。Redisson 提供了紅鎖實(shí)現(xiàn),但更復(fù)雜,通常用于對(duì)一致性要求極高的場景。

總結(jié)

對(duì)于大多數(shù) Spring Boot 項(xiàng)目,?我強(qiáng)烈推薦直接使用 Redisson。它功能完善、久經(jīng)考驗(yàn),能讓你遠(yuǎn)離分布式鎖的各種復(fù)雜細(xì)節(jié),更專注于業(yè)務(wù)邏輯開發(fā)。如果你是為了學(xué)習(xí)底層原理或?qū)崿F(xiàn)一個(gè)非常簡單的鎖,那么可以嘗試手動(dòng)實(shí)現(xiàn)的方式。

以上就是SpringBoot項(xiàng)目中實(shí)現(xiàn)Redis分布式鎖的方法與最佳實(shí)踐的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot實(shí)現(xiàn)Redis分布式鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Maven中Profile基礎(chǔ)知識(shí)與激活機(jī)制詳解

    Maven中Profile基礎(chǔ)知識(shí)與激活機(jī)制詳解

    這篇文章將深入解析Maven?Profile的底層實(shí)現(xiàn)機(jī)制,從Profile標(biāo)識(shí)符的作用域劃分到多維度激活條件的組合應(yīng)用,全面剖析其在不同規(guī)模項(xiàng)目中的最佳實(shí)踐,需要的可以了解下
    2025-05-05
  • java獲取文件擴(kuò)展名的方法小結(jié)【正則與字符串截取】

    java獲取文件擴(kuò)展名的方法小結(jié)【正則與字符串截取】

    這篇文章主要介紹了java獲取文件擴(kuò)展名的方法,結(jié)合實(shí)例形式分析了使用正則與字符串截取兩種獲取擴(kuò)展名的操作技巧,需要的朋友可以參考下
    2017-01-01
  • Java線程池高頻面試題總結(jié)

    Java線程池高頻面試題總結(jié)

    在進(jìn)程和線程的相關(guān)面試題中還有一部分是關(guān)于多線程和線程池的,也是在這一部分中比較??疾斓膬?nèi)容。本篇文章就帶你了解一下,希望能給你帶來幫助
    2021-08-08
  • SpringMvc獲取頁面中的參數(shù)方法詳解

    SpringMvc獲取頁面中的參數(shù)方法詳解

    這篇文章主要介紹了SpringMvc獲取頁面中的參數(shù)方法詳解,獲取頁面的參數(shù)通常都是讓類實(shí)現(xiàn)設(shè)置HttpServletRequest request接口然后重寫接口中的方法的辦法來得到參數(shù),但是在Springmvc中有其他的獲取方法,需要的朋友可以參考下
    2023-10-10
  • Java因項(xiàng)目配置不當(dāng)而引發(fā)的數(shù)據(jù)泄露

    Java因項(xiàng)目配置不當(dāng)而引發(fā)的數(shù)據(jù)泄露

    這篇文章主要介紹了Java因項(xiàng)目配置不當(dāng)而引發(fā)的數(shù)據(jù)泄露解決辦法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • Java的volatile和sychronized底層實(shí)現(xiàn)原理解析

    Java的volatile和sychronized底層實(shí)現(xiàn)原理解析

    文章詳細(xì)介紹了Java中的synchronized和volatile關(guān)鍵字的底層實(shí)現(xiàn)原理,包括字節(jié)碼層面、JVM層面的實(shí)現(xiàn)細(xì)節(jié),以及鎖的類型和MESI協(xié)議在多核處理器中的作用,文章還探討了synchronized和volatile的區(qū)別,以及如何通過Atomic類來實(shí)現(xiàn)更細(xì)粒度的原子操作,感興趣的朋友一起看看吧
    2025-03-03
  • SpringCloud分布式鏈路追蹤組件Sleuth配置詳解

    SpringCloud分布式鏈路追蹤組件Sleuth配置詳解

    這篇文章主要介紹了SpringCloud鏈路追蹤組件Sleuth配置方法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-11-11
  • 基于java查找最長字符串代碼實(shí)例

    基于java查找最長字符串代碼實(shí)例

    這篇文章主要介紹了基于java查找最長字符串代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Java中List集合去除重復(fù)數(shù)據(jù)的方法匯總

    Java中List集合去除重復(fù)數(shù)據(jù)的方法匯總

    這篇文章主要給大家介紹了關(guān)于Java中List集合去除重復(fù)數(shù)據(jù)的方法,文中通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • 如何使用Spring MVC的消息轉(zhuǎn)換器設(shè)置日期格式

    如何使用Spring MVC的消息轉(zhuǎn)換器設(shè)置日期格式

    這篇文章主要介紹了如何使用Spring MVC的消息轉(zhuǎn)換器設(shè)置日期格式,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07

最新評(píng)論

绥化市| 新绛县| 鲜城| 惠来县| 拜泉县| 商水县| 方山县| 望奎县| 河间市| 抚顺市| 且末县| 册亨县| 民县| 武义县| 葫芦岛市| 涞水县| 隆林| 聂荣县| 安阳市| 万安县| 吉林省| 米泉市| 闽侯县| 晋州市| 黔江区| 德惠市| 马山县| 玉龙| 宣恩县| 房山区| 商城县| 西乡县| 平湖市| 白玉县| 高阳县| 德化县| 江油市| 房产| 青冈县| 菏泽市| 昭觉县|