欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

引言

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

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

手動實現(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 請求標(biāo)識(可用UUID),用于安全釋放鎖
     * @param expireTime 鎖的過期時間(毫秒)
     * @param waitTime 獲取鎖的最大等待時間(毫秒)
     * @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) { // 在等待時間內(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; // 超時仍未獲取到鎖
    }

    /**
     * 釋放分布式鎖 (使用Lua腳本保證原子性)
     * @param lockKey 鎖的業(yè)務(wù)Key
     * @param requestId 請求標(biāo)識,必須與加鎖時一致
     * @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)鍵點:??

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

?在業(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(); // 生成唯一請求ID

        boolean locked = false;
        try {
            // 嘗試獲取鎖,等待5秒,鎖過期時間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)繁忙,請稍后再試");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("獲取鎖被中斷", e);
        } finally {
            // 釋放鎖,務(wù)必放在finally塊中確保執(zhí)行
            if (locked) {
                redisLock.unlock(lockKey, requestId);
            }
        }
    }
}

使用 Redisson(推薦)

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

?添加依賴?

pom.xml中添加 Redisson 依賴:

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

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

spring:
  redis:
    host: localhost
    port: 6379
# Redisson 通常會自動使用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對象

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

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

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

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

注意事項與最佳實踐

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

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

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

?Redis 模式?:

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

總結(jié)

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

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

相關(guān)文章

  • 登錄EasyConnect后無法通過jdbc訪問服務(wù)器數(shù)據(jù)庫問題的解決方法

    登錄EasyConnect后無法通過jdbc訪問服務(wù)器數(shù)據(jù)庫問題的解決方法

    描述一下近期使用EasyConnect遇到的問題,下面這篇文章主要給大家介紹了關(guān)于登錄EasyConnect后無法通過jdbc訪問服務(wù)器數(shù)據(jù)庫問題的解決方法,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-02-02
  • SpringBoot使用GTS的示例詳解

    SpringBoot使用GTS的示例詳解

    這篇文章主要介紹了SpringBoot使用GTS的示例詳解,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-10-10
  • 淺析Java中JSONObject和JSONArray使用

    淺析Java中JSONObject和JSONArray使用

    這篇文章主要介紹了Java中JSONObject和JSONArray使用的相關(guān)資料,需要的朋友可以參考下
    2016-06-06
  • Java設(shè)計模式之Prototype原型模式

    Java設(shè)計模式之Prototype原型模式

    這篇文章主要為大家詳細介紹了Java設(shè)計模式之Prototype原型模式的相關(guān)資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • java反射深入剖析(推薦)

    java反射深入剖析(推薦)

    下面小編就為大家?guī)硪黄猨ava反射深入剖析。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-07-07
  • Java使用html2image將html生成縮略圖圖片的實現(xiàn)示例

    Java使用html2image將html生成縮略圖圖片的實現(xiàn)示例

    本文主要介紹了Java使用html2image將html生成縮略圖圖片的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-12-12
  • Mybatis利用分頁插件PageHelper快速實現(xiàn)分頁查詢

    Mybatis利用分頁插件PageHelper快速實現(xiàn)分頁查詢

    如果你也在用MyBatis,建議嘗試該分頁插件,這一定是最方便使用的分頁插件,這篇文章主要給大家介紹了關(guān)于Mybatis利用分頁插件PageHelper快速實現(xiàn)分頁查詢的相關(guān)資料,PageHelper是一個Mybatis的分頁插件,負責(zé)將已經(jīng)寫好的sql語句,進行分頁加工,需要的朋友可以參考下
    2021-08-08
  • Java數(shù)組傳遞及可變參數(shù)操作實例詳解

    Java數(shù)組傳遞及可變參數(shù)操作實例詳解

    這篇文章主要介紹了Java數(shù)組傳遞及可變參數(shù)操作,結(jié)合實例形式詳細分析了java數(shù)組參數(shù)傳遞與可變參數(shù)相關(guān)使用技巧,需要的朋友可以參考下
    2019-09-09
  • 編輯器Ueditor和SpringBoot 的整合方法

    編輯器Ueditor和SpringBoot 的整合方法

    本文通過實例代碼給大家介紹了編輯器Ueditor和SpringBoot 的整合方法,需要的朋友參考下吧
    2017-08-08
  • Java集合TreeSet用法詳解

    Java集合TreeSet用法詳解

    本文詳細講解了Java集合TreeSet用法,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-12-12

最新評論