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

redis實現(xiàn)紅鎖的示例代碼

 更新時間:2025年04月08日 10:22:53   作者:morris131  
在分布式系統(tǒng)中,實現(xiàn)一個可靠的鎖機制是非常重要的,本文主要介紹了redis實現(xiàn)紅鎖的示例代碼,具有一定的參考價值,感興趣的可以了解一下

舉個真實的例子:你的團隊剛上線了一個秒殺系統(tǒng),用Redis鎖來防止超賣。測試環(huán)境明明跑得好好的,但大促當晚卻出現(xiàn)了100件庫存賣出了120單的靈異事件。查看日志才發(fā)現(xiàn):就在用戶瘋狂點擊的瞬間,Redis主節(jié)點突然掛了,新的主節(jié)點還沒拿到鎖的信息,結果兩個用戶同時搶到了"同一把鎖"。

這就是很多開發(fā)者踩過的坑——你以為用了Redis分布式鎖就萬事大吉,其實這些情況隨時可能讓鎖失效:

  • 主節(jié)點剛給你加完鎖就崩潰了,從節(jié)點接班時一臉懵:“什么鎖?我沒聽說過啊”
  • 你的程序正處理到一半突然卡住了(比如GC停頓),等回過神來鎖早就過期了
  • 網(wǎng)絡抽風導致鎖信息沒傳到位,多個客戶端都覺得自己拿到了鎖

為了解決這些頭疼問題,Redis作者提出了**紅鎖(RedLock)**方案。簡單來說就是"不要把雞蛋放在一個籃子里":讓多個獨立的Redis節(jié)點投票決定鎖的歸屬,只有半數(shù)以上同意才算真正拿到鎖。

但這套方案也引發(fā)過激烈爭論,有人甚至說它"數(shù)學上就不安全"。本文將用最直白的語言:

  • 先帶你看看傳統(tǒng)Redis鎖在集群環(huán)境為什么容易翻車
  • 拆解紅鎖這個"少數(shù)服從多數(shù)"的解決方案
  • 手把手教你用Java代碼實現(xiàn)紅鎖
  • 揭秘Redisson框架如何簡化紅鎖的使用

讀完本文你會明白:沒有完美的分布式鎖,只有適合場景的選擇。下次設計系統(tǒng)時,至少能清楚知道手里的鎖到底有幾成把握。

集群鎖的缺陷與挑戰(zhàn)

在Redis Cluster環(huán)境中,傳統(tǒng)的SETNX分布式鎖存在以下致命缺陷:主從切換導致鎖失效。

問題步驟復現(xiàn):

  • 客戶端A通過SET key random_val NX PX 30000主節(jié)點成功獲取鎖

  • 主節(jié)點宕機,Redis Cluster觸發(fā)故障轉移,從節(jié)點升級為新主節(jié)點

  • 由于Redis主從復制是異步的,鎖可能未同步到新主節(jié)點

  • 客戶端B向新主節(jié)點申請相同資源的鎖,成功獲取導致數(shù)據(jù)競爭

# 主節(jié)點寫入鎖
SET resource_1 8a3e72 NX PX 10000  
OK

# 主節(jié)點宕機,從節(jié)點晉升但未同步鎖數(shù)據(jù)
# 新主節(jié)點處理客戶端B的請求
SET resource_1 5b9fd2 NX PX 10000  
OK  # 鎖被重復獲?。?

紅鎖(RedLock)的設計與實現(xiàn)

N個獨立Redis節(jié)點(非Cluster模式)中,當客戶端在半數(shù)以上節(jié)點成功獲取鎖,且總耗時小于鎖有效期時,才認為鎖獲取成功。

實現(xiàn)步驟詳解

假設部署5個Redis節(jié)點(N=5):

  • 獲取當前時間:記錄開始時間T1(毫秒精度)

  • 依次向所有節(jié)點申請鎖

SET lock_key valueNX PX $ttl
  • value:全局唯一值(如UUID)
  • ttl:鎖自動釋放時間(如10秒)
  • 計算鎖有效性
  • 客戶端計算獲取鎖總耗時T_elapsed = T2 - T1(T2為最后響應時間)

  • 僅當以下兩個條件滿足時,鎖才有效:

    成功獲取鎖的節(jié)點數(shù) ≥ 3(N/2 + 1)

    T_elapsed < ttl(確保鎖未過期)

  • 加鎖成功,去操作共享資源

  • 釋放鎖:向所有節(jié)點發(fā)送Lua腳本刪除鎖(需驗證值)

if redis.call("get",KEYS[1]) == ARGV[1] then
   return redis.call("del",KEYS[1])
else
   return 0
end

NPC爭議問題

紅鎖算法自誕生起就伴隨著**N(網(wǎng)絡延遲)、P(進程暫停)、C(時鐘漂移)**三個核心爭議,這些現(xiàn)實世界中的不確定因素,動搖了紅鎖在數(shù)學意義上的絕對安全性。

網(wǎng)絡延遲(Network Delay)的致命時間差

問題場景

  • 客戶端在節(jié)點A、B、C成功獲取鎖,總耗時48ms(小于TTL 50ms)

  • 但由于跨機房網(wǎng)絡波動,實際鎖在節(jié)點上的有效時間存在差異:

    • 節(jié)點A記錄的鎖過期時間:客戶端本地時間+50ms = T+50
    • 節(jié)點B因網(wǎng)絡延遲,實際鎖過期時間為T+52
    • 節(jié)點C因網(wǎng)絡擁塞,實際鎖過期時間僅T+48
  • 在時間窗口T+48T+50之間,客戶端認為鎖仍有效,但節(jié)點C的鎖已提前失效

后果
其他客戶端可能在此期間獲取節(jié)點C的鎖,導致鎖狀態(tài)分裂,多個客戶端同時進入臨界區(qū)。

進程暫停(Process Pause)的「薛定諤鎖」

經(jīng)典案例

// 偽代碼:獲取鎖后執(zhí)行業(yè)務邏輯
if (redLock.tryLock()) {
    // 觸發(fā)Full GC暫停300ms
    System.gc(); 
    
    // 此時鎖已過期,但客戶端仍在寫數(shù)據(jù)
    updateInventory(); 
}

關鍵時間線

  • T0: 獲取鎖(TTL=200ms)
  • T0+100ms: 進入GC暫停,持續(xù)300ms
  • T0+400ms: GC結束,繼續(xù)執(zhí)行業(yè)務邏輯
  • 鎖實際在T0+200ms已失效,但客戶端在T0+400ms仍以為自己持有鎖

數(shù)據(jù)災難
其他客戶端在T0+200ms到T0+400ms期間可能修改數(shù)據(jù),導致最終結果錯亂。

時鐘漂移(Clock Drift)的時空扭曲

物理機時鐘偏移實驗數(shù)據(jù)

節(jié)點時鐘誤差范圍常見誘因
節(jié)點A±200ms/分鐘虛擬機時鐘不同步
節(jié)點B±500ms/天NTP服務異常
節(jié)點C±10秒/小時宿主機硬件時鐘故障

連鎖反應

  • 客戶端計算鎖有效期基于本地時鐘(假設為T+100ms)
  • 但節(jié)點B的時鐘比實際快30秒,導致其記錄的鎖過期時間為T-29000ms
  • 鎖在客戶端認為的有效期內提前被節(jié)點B自動釋放

行業(yè)領袖的正面交鋒

Martin Kleppmann(《數(shù)據(jù)密集型應用設計》作者)

“紅鎖依賴的假設——『客戶端能準確感知鎖存活時間』,在異步分布式系統(tǒng)中根本無法保證。即使沒有節(jié)點故障,NPC問題也會導致鎖狀態(tài)的不確定性。”

Antirez(Redis作者)的反駁

"工程實踐中可以通過以下手段控制風險:

使用帶溫度補償?shù)脑隅娪布?/p>

禁用NTP服務的時鐘跳變調整

監(jiān)控進程暫停(如GC日志分析)

為鎖TTL設置冗余緩沖時間(如額外20%)"

紅鎖的Java實現(xiàn)示例

使用Jedis客戶端實現(xiàn)紅鎖:

package com.morris.redis.demo.redlock;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 使用jedis手寫RedLock
 */
public class JedisRedLock {

    public static final int EXPIRE_TIME = 30_000;

    private final List<JedisPool> jedisPoolList;

    private final String lockKey;

    private final String lockValue;

    public JedisRedLock(List<JedisPool> jedisPoolList, String lockKey) {
        this.jedisPoolList = jedisPoolList;
        this.lockKey = lockKey;
        this.lockValue = UUID.randomUUID().toString();
    }

    public void lock() {
        while (!tryLock()) {
            try {
                TimeUnit.MILLISECONDS.sleep(100); // 失敗后短暫等待
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public boolean tryLock() {
        long startTime = System.currentTimeMillis();
        int successCount = 0;
        try {
            for (JedisPool jedisPool : jedisPoolList) {
                try (Jedis jedis = jedisPool.getResource();) {
                    // 原子化加鎖:SET lockKey UUID NX PX expireTime
                    String result = jedis.set(lockKey, lockValue,
                            SetParams.setParams().nx().px(EXPIRE_TIME));
                    if ("OK".equals(result)) {
                        successCount++;
                    }
                }
            }
            // 計算獲取鎖耗時
            long elapsedTime = System.currentTimeMillis() - startTime;

            // 驗證:多數(shù)節(jié)點成功 且 耗時小于TTL
            return successCount >= (jedisPoolList.size() / 2 + 1) && elapsedTime < EXPIRE_TIME;
        } finally {
            // 若加鎖失敗,立即釋放已獲得的鎖
            if (successCount < (jedisPoolList.size() / 2 + 1)) {
                unlock();
            }
        }
    }

    public void unlock() {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

        for (JedisPool jedisPool : jedisPoolList) {
            try (Jedis jedis = jedisPool.getResource()) {
                jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(lockValue));
            }
        }
    }

}

手寫RedLock的使用:

package com.morris.redis.demo.redlock;

import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 手寫RedLock的使用
 */
public class JedisRedLockDemo {

    private volatile static int count;

    public static void main(String[] args) throws InterruptedException {
        List<JedisPool> jedisPoolList = new ArrayList<>();
        jedisPoolList.add(new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6379));
        jedisPoolList.add(new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6380));
        jedisPoolList.add(new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6381));
        jedisPoolList.add(new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6382));
        jedisPoolList.add(new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6383));

        int threadCount = 3;
        CountDownLatch countDownLatch = new CountDownLatch(threadCount);
        ExecutorService executorService = Executors.newFixedThreadPool(threadCount);

        for (int i = 0; i < threadCount; i++) {
            executorService.submit(() -> {
                JedisRedLock jedisRedLock = new JedisRedLock(jedisPoolList, "lock-key");
                jedisRedLock.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + "獲得鎖,開始執(zhí)行業(yè)務邏輯。。。");
                    try {
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName() + "獲得鎖,結束執(zhí)行業(yè)務邏輯。。。");
                    count++;
                } finally {
                    jedisRedLock.unlock();
                }
                countDownLatch.countDown();
            });
        }

        countDownLatch.await();
        executorService.shutdown();
        System.out.println(count);
    }

}

Redisson中紅鎖的使用

Redisson已封裝紅鎖實現(xiàn),自動處理節(jié)點通信與鎖續(xù)期:

package com.morris.redis.demo.redlock;

import org.redisson.Redisson;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Redisson中紅鎖的使用
 */
public class RedissonRedLockDemo {

    private volatile static int count;

    public static void main(String[] args) throws InterruptedException {

        List<String> serverList = Arrays.asList("redis://127.0.0.1:6379", "redis://127.0.0.1:6380", "redis://127.0.0.1:6381",
                "redis://127.0.0.1:6382", "redis://127.0.0.1:6383");

        List<RedissonClient> redissonClientList = new ArrayList<>(serverList.size());
        for (String server : serverList) {
            Config config = new Config();
            config.useSingleServer()
                    .setAddress(server);

            redissonClientList.add(Redisson.create(config));
        }

        List<RLock> lockList = new ArrayList<>(redissonClientList.size());
        for (RedissonClient redissonClient : redissonClientList) {
            lockList.add(redissonClient.getLock("java-lock"));
        }
        
        int threadCount = 3;
        CountDownLatch countDownLatch = new CountDownLatch(threadCount);
        ExecutorService executorService = Executors.newFixedThreadPool(threadCount);

        for (int i = 0; i < threadCount; i++) {
            executorService.submit(() -> {
                RedissonRedLock redissonRedLock = new RedissonRedLock(lockList.toArray(new RLock[0]));
                redissonRedLock.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + "獲得鎖,開始執(zhí)行業(yè)務邏輯。。。");
                    try {
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(Thread.currentThread().getName() + "獲得鎖,結束執(zhí)行業(yè)務邏輯。。。");
                    count++;
                } finally {
                    redissonRedLock.unlock();
                }
                countDownLatch.countDown();
            });
        }

        countDownLatch.await();
        executorService.shutdown();
        System.out.println(count);

        for (RedissonClient redissonClient : redissonClientList) {
            redissonClient.shutdown();
        }
    }

}

Redisson優(yōu)勢

  • 自動續(xù)期:通過WatchDog機制延長鎖有效期
  • 簡化API:封裝底層細節(jié),支持異步/響應式編程
  • 故障容錯:自動跳過宕機節(jié)點,保證半數(shù)以上成功即可

總結

紅鎖通過多節(jié)點投票機制,顯著提升了分布式鎖的可靠性,但需權衡其實現(xiàn)復雜度與運維成本。建議在以下場景選擇紅鎖:

  • 需要跨機房/地域部署
  • 業(yè)務對數(shù)據(jù)一致性要求極高
  • 已具備獨立Redis節(jié)點運維能力

對于大多數(shù)場景,可優(yōu)先使用Redisson等成熟框架,避免重復造輪子。若對一致性有極致要求,可考慮ZooKeeper/etcd等基于共識算法的方案。

到此這篇關于redis實現(xiàn)紅鎖的示例代碼的文章就介紹到這了,更多相關redis實現(xiàn)紅鎖內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • redis常用命令、常見錯誤、配置技巧等分享

    redis常用命令、常見錯誤、配置技巧等分享

    這篇文章主要介紹了redis常用命令、常見錯誤、配置技巧等分享,本文分享了12條redis知識,需要的朋友可以參考下
    2015-02-02
  • CentOS系統(tǒng)安裝Redis及Redis的PHP擴展詳解

    CentOS系統(tǒng)安裝Redis及Redis的PHP擴展詳解

    這篇文章主要介紹了CentOS系統(tǒng)下安裝Redis數(shù)據(jù)的教程,以及詳解了Redis數(shù)據(jù)庫的PHP擴展,文中介紹的很詳細,相信對大家的理解和學習具有一定的參考借鑒價值,有需要的朋友們可以參考借鑒,下面來一起看看吧。
    2016-12-12
  • Redis2.8配置文件中文詳解

    Redis2.8配置文件中文詳解

    這篇文章主要介紹了Redis2.8配置文件中文詳解,本文提供的是是Redis2.8.9的配置文件各項的中文解釋,需要的朋友可以參考下
    2015-06-06
  • redis解決高并發(fā)看門狗策略的實現(xiàn)

    redis解決高并發(fā)看門狗策略的實現(xiàn)

    本文主要介紹了redis解決高并發(fā)看門狗策略的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-02-02
  • 大家都應該知道的Redis過期鍵與過期策略

    大家都應該知道的Redis過期鍵與過期策略

    這篇文章主要給大家介紹了一些應該知道的Redis過期鍵與過期策略的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Redis具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-11-11
  • redis?setIfAbsent返回null的問題及解決

    redis?setIfAbsent返回null的問題及解決

    這篇文章主要介紹了redis?setIfAbsent返回null的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Ubuntu22.04 LTS 上安裝Redis的過程

    Ubuntu22.04 LTS 上安裝Redis的過程

    Redis是一種開源的內存數(shù)據(jù)存儲,可以用作數(shù)據(jù)庫、緩存和消息代理等,本文將會介紹兩種不同的安裝方式,包括從源代碼編譯安裝以及通過apt包管理器安裝,需要的朋友參考下吧
    2023-11-11
  • Redis協(xié)議具體用法詳解

    Redis協(xié)議具體用法詳解

    在本篇文章中小編給大家整理了關于Redis協(xié)議具體用法以及相關內容知識點,需要的朋友們學習下。
    2019-06-06
  • Redis實現(xiàn)延遲任務的常見方案詳解

    Redis實現(xiàn)延遲任務的常見方案詳解

    延遲任務(Delayed?Task)是指在未來的某個時間點,執(zhí)行相應的任務,本文為大家整理了Redis實現(xiàn)延遲任務的幾個常見方案,希望對大家有所幫助
    2024-04-04
  • Redis和Lua實現(xiàn)分布式限流器的方法詳解

    Redis和Lua實現(xiàn)分布式限流器的方法詳解

    這篇文章主要給大家介紹了關于Redis和Lua實現(xiàn)分布式限流器的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Redis和Lua具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-06-06

最新評論