亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Redis作為分布式鎖的使用詳解

 更新時間:2025年05月13日 09:17:26   作者:找不到、了  
這篇文章主要介紹了Redis作為分布式鎖的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

分布式鎖是控制分布式系統(tǒng)或不同系統(tǒng)之間共同訪問共享資源的一種鎖實現(xiàn)。如果不同的系統(tǒng)或同一個系統(tǒng)的不同主機之間共享了某個資源時,往往通過互斥來防止彼此之間的干擾。

實現(xiàn)分布式鎖的方式有很多,可以通過各種中間件來進行分布式鎖的設計,包括Redis、Zookeeper等。

如下圖所示:

1、實現(xiàn)鎖的方法

如下圖所示鎖的流程:

1.1. setnx命令

屬于最簡單的鎖,不推薦生產(chǎn)使用。

SETNX toilet_1 "occupied"  # 嘗試鎖門
  • 如果返回1:成功
  • 如果返回0:失敗

問題:如果客戶端崩,永遠被占著(死鎖)。

1.2. 帶過期時間的鎖

屬于對setnx命令的改進版:

SETNX toilet_1 "occupied"
EXPIRE toilet_1 30  # 30秒后自動解鎖

仍然有問題:兩條命令不是原子的,可能在SETNX和EXPIRE之間崩潰。

1.3. 原子命令(推薦)

該命令可使用于生產(chǎn)級方案:

SET toilet_1 "user_123" NX EX 30  # 原子操作:鎖門+設置30秒自動開鎖

1.4. RedLock算法詳解

當需要更高可靠性時,Redis作者Antirez提出的分布式鎖算法:

1.實現(xiàn)原理

獲取當前毫秒級時間戳T1

依次向N個獨立的Redis實例申請鎖

計算獲取鎖總耗時 = 當前時間T2 - T1

  • 必須小于鎖有效時間
  • 必須獲得多數(shù)(N/2+1)節(jié)點認可

鎖實際有效時間 = 初始設置時間 - 獲取鎖總耗時。

代碼示例:

// RedissonRedLock.tryLock()的核心邏輯
while (waitTimeRemaining > 0) {
    long start = System.nanoTime();
    
    // 嘗試從多數(shù)節(jié)點獲取鎖
    int acquiredLocks = tryAcquireMultipleLocks();
    
    if (acquiredLocks >= majority) {
        // 計算實際有效時間
        long elapsed = System.nanoTime() - start;
        long lockTime = leaseTime - TimeUnit.NANOSECONDS.toMillis(elapsed);
        
        if (lockTime > 0) {
            // 對所有成功節(jié)點設置統(tǒng)一過期時間
            scheduleLockExpiration(lockTime);
            return true;
        }
        // 超時則釋放已獲得的鎖
        releaseAcquiredLocks();
    }
    
    // 等待隨機時間后重試
    waitTimeRemaining -= calculateWaitTime();
}

2.設計目的

  • 當單個Redis節(jié)點宕機時,系統(tǒng)仍能正常工作
  • 防止主從切換時的鎖失效(原主節(jié)點崩潰,從節(jié)點晉升但未同步鎖信息)

3.關鍵保障機制

  • 時鐘同步:所有Redis節(jié)點必須時間同步(NTP)
  • 過期時間補償:扣除鎖獲取耗時
  • 多數(shù)派原則:至少(N/2 + 1)個節(jié)點確認

4.局限性

1. 仍然存在的競爭問題

2. 網(wǎng)絡分區(qū)問題

當發(fā)生網(wǎng)絡分區(qū)時:

  • 客戶端可能無法感知部分節(jié)點狀態(tài)
  • 可能出現(xiàn)多個客戶端同時認為自己持有鎖

3. 性能開銷

獲取多個鎖的延遲顯著高于單節(jié)點:

  • 通常比單節(jié)點慢3-5倍
  • 不適合高頻短時鎖場景

而對于RedLock的本質(zhì)作用確實主要是為了解決單點故障問題,而不是提升并發(fā)性能,并未徹底解決一致性,如果要解決一致性問題,需要結合防護令牌或分布式事務。

1.5. 防護令牌(Fencing Token)模式

當發(fā)生鎖競爭的時候,假設5節(jié)點RedLock:

  • 客戶端A獲得節(jié)點1、2、3的鎖
  • 客戶端B獲得節(jié)點3、4、5的鎖

此時:

  • 兩個客戶端都認為自己獲得了鎖(都獲得3個節(jié)點)
  • 實際發(fā)生了沖突(節(jié)點3被雙方認為屬于自己)

代碼示例:

// 獲取鎖時返回單調(diào)遞增的token
LockResult result = redLock.tryLockWithToken();
long token = result.getToken();

// 操作資源時驗證token
if (resource.getCurrentToken() < token) {
    resource.write(data, token);
} else {
    throw new ConcurrentModificationException();
}
  • 實際實現(xiàn)中會加入** fencing token(防護令牌)機制
  • 每次鎖獲取附帶單調(diào)遞增的token
  • 資源操作時需要驗證token順序。

1.6. 看門狗機制

在上述的章節(jié)了解到,防護令牌可以解決鎖競爭一致性的問題,那么如果在鎖執(zhí)行過程中,過期時間到期,而業(yè)務還沒執(zhí)行完,那么該怎么辦呢?

看門狗(Watchdog)機制是Redis分布式鎖中確保業(yè)務執(zhí)行期間鎖不會意外釋放的關鍵設計,尤其在Redisson等客戶端中廣泛使用。

當業(yè)務執(zhí)行時間超過鎖的初始過期時間時,防止其他客戶端提前獲取鎖導致數(shù)據(jù)競爭。

流程:

// 獲取鎖(默認30秒看門狗時間)
RLock lock = redisson.getLock("order_lock");
lock.lock(); // 內(nèi)部啟動看門狗線程

try {
    // 執(zhí)行業(yè)務邏輯(可能超過30秒)
    processOrder();
} finally {
    lock.unlock(); // 釋放時會停止看門狗
}

鎖獲取時

public void lock() {
    try {
        lockInterruptibly();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}

public void lockInterruptibly() throws InterruptedException {
    // 嘗試獲取鎖,默認leaseTime=30秒
    tryAcquireAsync(leaseTime, TimeUnit.MILLISECONDS).sync();
    
    // 啟動看門狗線程
    scheduleExpirationRenewal();
}

看門狗線程

protected void scheduleExpirationRenewal() {
    Thread renewalThread = new Thread(() -> {
        while (!Thread.currentThread().isInterrupted()) {
            // 每10秒(leaseTime/3)續(xù)期一次
            try {
                Thread.sleep(leaseTime / 3);
                
                // 通過Lua腳本續(xù)期
                String script = 
                    "if redis.call('hexists', KEYS[1], ARGV[2]) == 1 then " +
                    "return redis.call('pexpire', KEYS[1], ARGV[1]); " +
                    "else return 0; end";
                
                redis.eval(script, 
                    Collections.singletonList(getName()),
                    internalLockLeaseTime, getLockName(Thread.currentThread().getId()));
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    });
    renewalThread.start();
}

參數(shù)和配置方式如下:

jpg jpg

2、使用場景

用一個電影院搶座的例子,通過Java代碼展示Redis分布式鎖的實際應用。這個場景非常貼近生活,容易理解分布式鎖的必要性。

假設有一個熱門電影場次,多個用戶同時在線選座,我們需要保證:

  • 一個座位只能被一個用戶選中
  • 用戶有10分鐘支付時間
  • 超時未支付自動釋放座位

1、基礎配置

首先添加Redis和Redisson(Redis Java客戶端)依賴:

<!-- pom.xml -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.16.8</version>
</dependency>

初始化Redis連接:

public class RedisLockDemo {
    private static RedissonClient redisson;
    
    static {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        redisson = Redisson.create(config);
    }
}

簡單實現(xiàn):選座鎖

1. 獲取座位鎖

public boolean lockSeat(String seatNumber, String userId) {
    // 獲取分布式鎖對象
    RLock lock = redisson.getLock("seat:" + seatNumber);
    
    try {
        // 嘗試加鎖,waitTime=0表示不等待,leaseTime=10分鐘自動解鎖
        return lock.tryLock(0, 10, TimeUnit.MINUTES);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        return false;
    }
}

2. 釋放座位鎖

public void unlockSeat(String seatNumber, String userId) {
    RLock lock = redisson.getLock("seat:" + seatNumber);
    
    // 檢查是否還被當前線程持有
    if (lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

3. 完整選座流程

public boolean selectSeat(String seatNumber, String userId) {
    if (!lockSeat(seatNumber, userId)) {
        System.out.println(userId + " 搶座失敗,座位已被鎖定");
        return false;
    }
    
    try {
        System.out.println(userId + " 成功鎖定座位 " + seatNumber);
        // 模擬用戶支付流程
        boolean paid = mockPaymentProcess(userId);
        
        if (paid) {
            System.out.println(userId + " 支付成功,座位確認");
            return true;
        } else {
            System.out.println(userId + " 支付超時,座位釋放");
            return false;
        }
    } finally {
        unlockSeat(seatNumber, userId);
    }
}

private boolean mockPaymentProcess(String userId) {
    // 模擬50%概率支付成功
    try {
        Thread.sleep(2000); // 模擬支付思考時間
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    return new Random().nextBoolean();
}

3、高級特性:鎖續(xù)期

當用戶支付時間可能超過10分鐘時,需要自動續(xù)期:

public boolean lockSeatWithRenewal(String seatNumber, String userId) {
    RLock lock = redisson.getLock("seat:" + seatNumber);
    
    try {
        // 獲取鎖,并設置看門狗自動續(xù)期(默認30秒)
        lock.lock();
        
        // 啟動一個線程定期續(xù)期
        new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    Thread.sleep(5000); // 每5秒續(xù)期一次
                    lock.expire(10, TimeUnit.MINUTES); // 續(xù)期10分鐘
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }).start();
        
        return true;
    } catch (Exception e) {
        return false;
    }
}

4、測試用例

public static void main(String[] args) {
    RedisLockDemo demo = new RedisLockDemo();
    
    // 模擬3個用戶同時搶5號座位
    new Thread(() -> demo.selectSeat("A05", "用戶1")).start();
    new Thread(() -> demo.selectSeat("A05", "用戶2")).start();
    new Thread(() -> demo.selectSeat("A05", "用戶3")).start();
}

輸出可能結果:

用戶1 成功鎖定座位 A05用戶2 搶座失敗,座位已被鎖定用戶3 搶座失敗,座位已被鎖定用戶1 支付成功,座位確認

5、關鍵點解析

  • 鎖的Key設計seat:A05 明確表示對A05座位的鎖
  • 唯一標識:雖然沒有直接使用userId作為value,但Redisson內(nèi)部會維護線程與鎖的關系
  • 自動釋放:即使程序崩潰,10分鐘后鎖也會自動釋放
  • 可重入性:同一個線程可以多次獲取同一把鎖(Redisson特性)

6、對比生活場景

技術概念電影院例子
Redis服務器電影院售票系統(tǒng)
分布式鎖座位鎖定狀態(tài)
鎖的Key座位號(如A05)
鎖的Value售票員記錄的本子(誰鎖的)
鎖過期時間"保留座位10分鐘"的告示牌
獲取鎖失敗看到座位已經(jīng)被標記"已預訂"
鎖續(xù)期顧客請求延長保留時間

這個例子展示了:

  • 如何用Redis解決現(xiàn)實中的資源競爭問題
  • Java中實際使用Redis分布式鎖的代碼寫法
  • 處理鎖超時、續(xù)期等常見場景的方法

通過電影院選座這種熟悉的場景,應該能更直觀地理解Redis分布式鎖的工作機制了。實際開發(fā)中,使用Redisson等成熟客戶端可以避免很多邊界條件的處理。

3、Redis分布式鎖的局限性

時鐘漂移問題

  • 依賴系統(tǒng)時鐘,多節(jié)點時鐘不同步可能影響RedLock

持久化延遲

  • 異步復制可能導致主節(jié)點崩潰后從節(jié)點丟失鎖信息

長時間阻塞

  • 獲取不到鎖的客戶端需要合理處理等待/超時

4、對比

總結

Redis分布式鎖憑借其優(yōu)異的性能和足夠的可靠性,已成為互聯(lián)網(wǎng)公司的首選方案。理解其實現(xiàn)原理和限制條件,能夠幫助我們在不同業(yè)務場景中做出合理的技術選型。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • Redis的持久化方式

    Redis的持久化方式

    Redis提供了兩種主要的持久化方式:RDB和AOF,RDB通過定時快照的方式保存數(shù)據(jù)狀態(tài),而AOF記錄每個寫操作以便于重啟時重放,兩者可以結合使用,且在重啟時AOF文件會被優(yōu)先用于數(shù)據(jù)恢復,RDB快照具有速度快、節(jié)省磁盤空間的優(yōu)點,但可能會丟失最近的數(shù)據(jù)
    2024-10-10
  • Redis實現(xiàn)限量優(yōu)惠券的秒殺功能

    Redis實現(xiàn)限量優(yōu)惠券的秒殺功能

    文章詳細分析了避免超賣問題的方法,包括確保一人一單的業(yè)務邏輯,并提供了代碼實現(xiàn)步驟和代碼示例,感興趣的朋友跟隨小編一起看看吧
    2024-12-12
  • 詳解Redis中的List類型

    詳解Redis中的List類型

    這篇文章主要介紹了Redis中的List類型,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • 淺談Redis阻塞的9種情況

    淺談Redis阻塞的9種情況

    本文主要介紹了淺談Redis阻塞的9種情況,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-03-03
  • 64位Windows下安裝Redis教程

    64位Windows下安裝Redis教程

    這篇文章主要介紹了64位Windows下安裝Redis教程,本文使用Microsoft Open Tech group 在 GitHub上開發(fā)的一個Win64版本的Redis,需要的朋友可以參考下
    2014-09-09
  • Redis實現(xiàn)RBAC權限管理

    Redis實現(xiàn)RBAC權限管理

    本文主要介紹了Redis實現(xiàn)RBAC權限管理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-03-03
  • redis緩存一致性延時雙刪代碼實現(xiàn)方式

    redis緩存一致性延時雙刪代碼實現(xiàn)方式

    這篇文章主要介紹了redis緩存一致性延時雙刪代碼實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 淺談Redis緩存更新策略

    淺談Redis緩存更新策略

    這篇文章主要介紹了Redis緩存更新策略的相關資料,講解的十分細致,有需要的小伙伴可以參考下
    2022-08-08
  • redis事務執(zhí)行常用命令及watch監(jiān)視詳解

    redis事務執(zhí)行常用命令及watch監(jiān)視詳解

    這篇文章主要為大家介紹了redis事務執(zhí)行常用命令及watch監(jiān)視詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-11-11
  • 基于Redis的List實現(xiàn)特價商品列表功能

    基于Redis的List實現(xiàn)特價商品列表功能

    本文通過場景分析給大家介紹了基于Redis的List實現(xiàn)特價商品列表,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2021-08-08

最新評論