偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Redisson簡明教程—你家的鎖芯該換了

數(shù)據(jù)庫 其他數(shù)據(jù)庫
今天咱們就來聊聊Redisson提供的各種鎖,Redisson就像是Redis給Java程序員的一把瑞士軍刀,不僅能存數(shù)據(jù),還能玩出各種分布式花樣。

1.簡介

2.看門狗

  2-1.為什么需要這個"狗子"?

  2-2.工作原理

  2-3.如何“擼狗子”

3.可重入鎖:你的分布式"萬能鑰匙"

  3-1.Redisson可重入鎖的魔法

  3-2.使用姿勢大全

  3-3.公平鎖

  3-4.非公平鎖

  3-5.可重入原理深扒

4.聯(lián)鎖:分布式鎖中的"全家桶套餐"

  4-1.聯(lián)鎖是什么?——"要么全有,要么全無"的霸道總裁

  4-2.為什么需要聯(lián)鎖?

  4-3.聯(lián)鎖使用三件套

  4-4.聯(lián)鎖的硬核原理

  4-5.聯(lián)鎖的三大禁忌

5.Redisson讀寫鎖:分布式系統(tǒng)中的"讀寫分離"高手

  5-1.使用姿勢

  5-2.原理深扒

6.信號量(Semaphore):分布式系統(tǒng)中的"限量入場券"

  6-1.使用姿勢大全

  6-2.原理深扒

7.紅鎖(RedLock):分布式鎖界的“聯(lián)合國維和部隊”

  7-1.核心原理

  7-2.注意事項

  7-3.使用姿勢

8.閉鎖(CountDownLatch):分布式系統(tǒng)中的"集結(jié)號"

  8-1.使用姿勢

  8-2.原理深扒

9.總結(jié)

1.簡介

各位攻城獅們,你還在使用原生命令來上鎖么?看來你還是不夠懶,餃子都給你包好了,你非要吃大餅配炒韭菜,快點改善一下“伙食”吧,寫代碼也要來點幸福感。今天咱們就來聊聊Redisson提供的各種鎖,Redisson就像是Redis給Java程序員的一把瑞士軍刀,不僅能存數(shù)據(jù),還能玩出各種分布式花樣。

  • Redis版本:Redis 2.8+,理想版本5.0+(支持 Stream、模塊化等高級特性,Redisson 能秀出全部技能)。
  • 架構(gòu)模式:支持單機、哨兵和集群(集群模式可靠性更高)

2.看門狗

想象你上廁所(獲取鎖)時帶著一只忠心耿耿的阿黃(看門狗)。當你蹲坑時間快到時(鎖快要過期),阿黃就會大叫:"主人你還沒完事嗎?我給你續(xù)時間啦!"(自動續(xù)期)

2-1.為什么需要這個"狗子"?

  • 防止業(yè)務沒執(zhí)行完鎖就過期:默認鎖30秒過期,但萬一你的業(yè)務要31秒呢?
  • 避免鎖丟失:如果客戶端崩潰,看門狗停止續(xù)期,鎖最終會自動釋放
  • 不用手動計算業(yè)務時間:再也不用戰(zhàn)戰(zhàn)兢兢估算業(yè)務執(zhí)行時間了

2-2.工作原理

圖片

  • 首次加鎖:默認設置鎖過期時間30秒
  • 啟動看門狗:加鎖成功后啟動一個定時任務(后臺線程)
  • 定期續(xù)期:每10秒(過期時間的1/3)檢查業(yè)務是否完成。未完成:執(zhí)行expire命令把鎖再續(xù)30秒;已完成:停止續(xù)期。
  • 最終釋放:業(yè)務完成調(diào)用unlock或客戶端斷開連接時釋放

2-3.如何“擼狗子”

Config config = new Config();
config.setLockWatchdogTimeout(30000L); // 單位毫秒,默認就是30秒
// ...
RedissonClient redisson = Redisson.create(config);
// 你也可以在加鎖時指定(會覆蓋默認值)
lock.lock(60, TimeUnit.SECONDS); // 這時看門狗會按60秒周期續(xù)期

3.可重入鎖:你的分布式"萬能鑰匙"

我們都知道Java中ReentrantLock和synchronized都是可重入鎖,但都只能用于單機環(huán)境的,在分布式環(huán)境下,Redisson給我提供了類似體驗的可重入鎖。

3-1.Redisson可重入鎖的魔法

  • 線程安全:不同JVM的相同線程也可重入
  • 自動續(xù)期:看門狗機制保活(默認30秒)
  • 公平/非公平:兩種模式可選
  • 超時機制:避免無限等待

3-2.使用姿勢大全

基礎款(阻塞式):
RLock lock = redisson.getLock("orderLock");
lock.lock(); // 一直等到天荒地老
try {
    // 你的核心業(yè)務
} finally {
    lock.unlock(); // 一定要放在finally!
}
高級款(嘗試獲取):
if (lock.tryLock(3, 30, TimeUnit.SECONDS)) { // 最多等3秒,鎖30秒自動過期
    try {
        // 業(yè)務處理
    } finally {
        lock.unlock();
    }
} else {
    log.warn("獲取鎖失敗,換個姿勢再試一次");
}
騷操作款(異步獲取):
RFuture<Void> lockFuture = lock.lockAsync();
lockFuture.whenComplete((res, ex) -> {
    if (ex == null) {
        try {
            // 異步業(yè)務處理
        } finally {
            lock.unlock();
        }
    }
});

3-3.公平鎖

  • 排隊機制:使用Redis的List結(jié)構(gòu)維護等待隊列
  • 訂閱發(fā)布:通過Redis的pub/sub通知下一個等待者
  • 雙重檢查:獲取鎖時檢查自己是否在隊列頭部
RLock fairLock = redisson.getFairLock("myFairLock");
try {
    fairLock.lock();
    // 業(yè)務邏輯
} finally {
    fairLock.unlock();
}

3-4.非公平鎖

  • 直接競爭:所有線程同時嘗試CAS操作
  • 效率優(yōu)先:沒有隊列維護開銷
  • 可能饑餓:運氣差的線程可能長期得不到鎖
RLock nonFairLock = redisson.getLock("hotItemLock");
if (nonFairLock.tryLock(50, TimeUnit.MILLISECONDS)) { // 拼手速!
    try {
        // 秒殺業(yè)務邏輯
    } finally {
        nonFairLock.unlock();
    }
}

3-5.可重入原理深扒

加鎖Lua腳本偽代碼:

-- 參數(shù):鎖key、鎖超時時間、客戶端ID+線程ID
if (redis.call('exists', KEYS[1]) == 0) then
    -- 鎖不存在,直接獲取
    redis.call('hset', KEYS[1], ARGV[2], 1);
    redis.call('pexpire', KEYS[1], ARGV[1]);
    returnnil;
end;

if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
    -- 重入情況:計數(shù)器+1
    redis.call('hincrby', KEYS[1], ARGV[2], 1);
    redis.call('pexpire', KEYS[1], ARGV[1]);
    returnnil;
end;

-- 鎖被其他線程持有
return redis.call('pttl', KEYS[1]); -- 返回剩余過期時間

解鎖Lua腳本偽代碼:

-- 參數(shù):鎖key、客戶端ID+線程ID
if (redis.call('hexists', KEYS[1], ARGV[1]) == 0) then
    -- 壓根沒持有鎖
    returnnil;
end;

-- 重入次數(shù)-1
local counter = redis.call('hincrby', KEYS[1], ARGV[1], -1);
if (counter > 0) then
    -- 還有重入次數(shù),更新過期時間
    redis.call('pexpire', KEYS[1], 30000);
    return0;
else
    -- 最后一次解鎖,刪除key
    redis.call('del', KEYS[1]);
    -- 發(fā)布解鎖消息
    redis.call('publish', KEYS[2], ARGV[2]);
    return1;
end;

4.聯(lián)鎖:分布式鎖中的"全家桶套餐"

4-1.聯(lián)鎖是什么?——"要么全有,要么全無"的霸道總裁

想象你要同時約三個女神約會:

  • 女神A:周末有空 ?
  • 女神B:周末有空 ?
  • 女神C:周末要加班 ? Redisson聯(lián)鎖的做法是:只要有一個拒絕,就取消所有約會!這就是聯(lián)鎖的"All or Nothing"哲學。

4-2.為什么需要聯(lián)鎖?

典型場景:
  • 跨資源事務:需要同時鎖定訂單、庫存、優(yōu)惠券三個系統(tǒng)
  • 數(shù)據(jù)一致性:確保多個關聯(lián)資源同時被保護
  • 避免死鎖:防止交叉等待導致的死鎖情況

4-3.聯(lián)鎖使用三件套

基本用法:

// 準備三把鎖(就像三個女神的聯(lián)系方式)
RLock lock1 = redisson.getLock("order_lock");
RLock lock2 = redisson.getLock("stock_lock");
RLock lock3 = redisson.getLock("coupon_lock");

// 創(chuàng)建聯(lián)鎖"約會套餐"
RedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2, lock3);

try {
    // 嘗試同時鎖定(約三位女神)
    if (multiLock.tryLock(3, 30, TimeUnit.SECONDS)) {
        // 三位都同意了!開始你的表演
        processOrder();
        updateStock();
        useCoupon();
    } else {
        log.warn("有個女神拒絕了你");
    }
} finally {
    multiLock.unlock(); // 記得送她們回家
}

高階技巧:

// 動態(tài)構(gòu)造聯(lián)鎖(適合不確定數(shù)量的資源)
List<RLock> locks = resourceIds.stream()
    .map(id -> redisson.getLock("resource_" + id))
    .collect(Collectors.toList());

RedissonMultiLock dynamicLock = new RedissonMultiLock(locks.toArray(new RLock[0]));

4-4.聯(lián)鎖的硬核原理

加鎖流程:
  • 順序加鎖:按傳入鎖的順序依次嘗試獲取
  • 失敗回滾:任意一個鎖獲取失敗時,釋放已獲得的所有鎖
  • 統(tǒng)一過期時間:所有鎖使用相同的過期時間
底層Lua腳本(簡化版):
-- 參數(shù):多個鎖的KEYS,統(tǒng)一過期時間,線程標識
local failed = false
for i, key inipairs(KEYS) do
    if redis.call('setnx', key, ARGV[2]) == 0then
        failed = true
        break
    end
    redis.call('expire', key, ARGV[1])
end

if failed then
    -- 釋放已經(jīng)獲取的鎖
    for j = 1, i-1do
        redis.call('del', KEYS[j])
    end
    return0
end
return1

4-5.聯(lián)鎖的三大禁忌

亂序使用(導致死鎖):

// 線程1:
multiLock(lockA, lockB).lock();

// 線程2:
multiLock(lockB, lockA).lock(); // 危險!可能死鎖

? 正確做法:全局統(tǒng)一加鎖順序

混合鎖類型:

// 混合普通鎖和公平鎖
new MultiLock(lock1, fairLock2); // 不推薦

? 正確做法:使用相同特性的鎖組合

忽略部分鎖失敗:

if (!multiLock.tryLock()) {
    // 直接返回,不處理部分獲取成功的情況
    return; // 危險!
}

? 正確做法:確保完全獲取或完全失敗

5.Redisson讀寫鎖:分布式系統(tǒng)中的"讀寫分離"高手

也許單機版的ReentrantReadWriteLock你聽說過,但是分布式環(huán)境下的版本可能很少接觸到。

典型場景:
  • 讀多寫少系統(tǒng):比如商品詳情頁(每秒上萬次讀取,每分鐘幾次更新)
  • 數(shù)據(jù)一致性要求:保證讀取時不會讀到半成品數(shù)據(jù)
  • 系統(tǒng)性能優(yōu)化:避免讀操作被不必要的串行化

5-1.使用姿勢

RReadWriteLock rwLock = redisson.getReadWriteLock("libraryBook_123");

// 讀操作(多個線程可同時進入)
rwLock.readLock().lock();
try {
    // 查詢數(shù)據(jù)(安全讀取)
    Book book = getBookFromDB(123);
} finally {
    rwLock.readLock().unlock();
}

// 寫操作(獨占訪問)
rwLock.writeLock().lock();
try {
    // 修改數(shù)據(jù)(安全寫入)
    updateBookInDB(123, newVersion);
} finally {
    rwLock.writeLock().unlock();
}

5-2.原理深扒

加讀鎖Lua腳本(簡化):

-- 檢查是否可以加讀鎖(沒有寫鎖或當前線程持有寫鎖)
if redis.call('hget', KEYS[1], 'mode') == 'write' then
    -- 如果有寫鎖且不是當前線程持有,則失敗
    if redis.call('hexists', KEYS[1], ARGV[2]) == 0 then
        return 0;
    end;
end;

-- 增加讀鎖計數(shù)
redis.call('hincrby', KEYS[1], ARGV[1], 1);
redis.call('pexpire', KEYS[1], ARGV[3]);
return 1;

加寫鎖Lua腳本(簡化):

-- 檢查是否已有鎖
if redis.call('exists', KEYS[1]) == 1 then
    -- 如果是讀模式或有其他寫鎖
    if redis.call('hget', KEYS[1], 'mode') == 'read' or
       redis.call('hlen', KEYS[1]) > 1 then
        return0;
    end;
    
    -- 如果是當前線程持有的寫鎖(重入)
    if redis.call('hexists', KEYS[1], ARGV[2]) == 1 then
        redis.call('hincrby', KEYS[1], ARGV[2], 1);
        return1;
    end;
end;

-- 獲取寫鎖
redis.call('hset', KEYS[1], 'mode', 'write');
redis.call('hset', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[3]);
return1;

6.信號量(Semaphore):分布式系統(tǒng)中的"限量入場券"

同樣的味道,Java中Semaphore的分布式版本。

6-1.使用姿勢大全

基礎用法:

// 獲取信號量(初始10個許可)
RSemaphore semaphore = redisson.getSemaphore("apiLimit");
semaphore.trySetPermits(10); // 設置許可數(shù)量

// 獲取許可(阻塞直到可用)
semaphore.acquire();
try {
    // 執(zhí)行業(yè)務(保證最多10個并發(fā))
    callLimitedAPI();
} finally {
    semaphore.release(); // 記得歸還!
}

// 嘗試獲?。ǚ亲枞?if (semaphore.tryAcquire()) {
    try {
        // 搶到許可了!
    } finally {
        semaphore.release();
    }
} else {
    log.warn("系統(tǒng)繁忙,請稍后再試");
}

高級技巧:

// 帶超時的嘗試獲取
if (semaphore.tryAcquire(3, 500, TimeUnit.MILLISECONDS)) {
    try {
        // 在500ms內(nèi)獲取到3個許可
        batchProcess();
    } finally {
        semaphore.release(3); // 歸還多個許可
    }
}

// 動態(tài)調(diào)整許可數(shù)量
semaphore.addPermits(5); // 增加5個許可(擴容)
semaphore.reducePermits(3); // 減少3個許可(縮容)

6-2.原理深扒

獲取許可的Lua腳本(簡化):

-- 參數(shù):信號量key、請求許可數(shù)
local value = redis.call('get', KEYS[1])
if value >= ARGV[1] then
    return redis.call('decrby', KEYS[1], ARGV[1])
else
    return -1
end

釋放許可的Lua腳本(簡化):

-- 參數(shù):信號量key、釋放許可數(shù)
return redis.call('incrby', KEYS[1], ARGV[1])

7.紅鎖(RedLock):分布式鎖界的“聯(lián)合國維和部隊”

RedLock 的誕生是為了對抗單點 Redis 掛掉后鎖失效的問題。它的目標就是:“即使部分節(jié)點掛了,我也要穩(wěn)如老狗”。

7-1.核心原理

Redisson 的 RedLock 實現(xiàn)來源于 Redis 作者 antirez 提出的 Redlock 算法。流程如下:

  • 準備多個獨立的 Redis 節(jié)點(注意:是互相獨立的,不是主從復制結(jié)構(gòu))。
  • 客戶端依次向這些節(jié)點嘗試加鎖(使用 SET NX PX 命令)。
  • 記錄耗時:加鎖操作總共不能超過鎖過期時間的 1/2(比如設置鎖有效期 10 秒,那就必須 5 秒內(nèi)搞定加鎖)。
  • 加鎖成功節(jié)點超過半數(shù)(N/2 + 1)視為成功。
  • 若失敗,立刻釋放所有加鎖成功的節(jié)點,以避免資源死鎖。
  • 釋放鎖時,同樣要向所有節(jié)點發(fā)送 unlock 操作。

7-2.注意事項

  • RedLock 是為多主 Redis 實例準備的,不是給 Redis Cluster 用的。
  • 你得維護多個彼此獨立的 Redis 實例,部署和運維成本更高。
  • RedLock 的“強一致性”并非線性一致性,它只是通過多點確認提升“高可用性”。

7-3.使用姿勢

// 準備多個獨立的RLock實例
RLock lock1 = redissonClient1.getLock("lock");
RLock lock2 = redissonClient2.getLock("lock"); 
RLock lock3 = redissonClient3.getLock("lock");

// 構(gòu)造紅鎖(建議奇數(shù)個,通常3/5個)
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);

try {
    // 嘗試獲取鎖(等待時間100s,鎖持有時間30s)
    boolean locked = redLock.tryLock(100, 30, TimeUnit.SECONDS);
    if (locked) {
        // 執(zhí)行業(yè)務邏輯
        doCriticalWork();
    }
} finally {
    redLock.unlock();
}

8.閉鎖(CountDownLatch):分布式系統(tǒng)中的"集結(jié)號"

一樣是Java的CountDownLatch的分布式版本,用法也是基本一樣。

8-1.使用姿勢

// 主協(xié)調(diào)節(jié)點(教官)
RCountDownLatch latch = redisson.getCountDownLatch("batchTaskLatch");
latch.trySetCount(5); // 需要等待5個任務

// 工作節(jié)點(學員)
RCountDownLatch workerLatch = redisson.getCountDownLatch("batchTaskLatch");
workerLatch.countDown(); // 完成任務時調(diào)用

// 主節(jié)點等待(在另一個線程/JVM)
latch.await(); // 阻塞直到計數(shù)器歸零
System.out.println("所有任務已完成!");

8-2.原理深扒

關鍵操作偽代碼:

-- countDown操作
local remaining = redis.call('decr', KEYS[1])
if remaining <= 0then
    redis.call('publish', KEYS[2], '0') -- 通知所有等待者
    redis.call('del', KEYS[1]) -- 清理計數(shù)器
end
return remaining

-- await操作
local count = redis.call('get', KEYS[1])
if count == falseortonumber(count) <= 0then
    return1-- 已經(jīng)完成
end
return0-- 需要繼續(xù)等待

9.總結(jié)

Redisson 提供了豐富的分布式鎖實現(xiàn),適用于各種分布式場景,使用體驗更好,選擇鎖類型時應根據(jù)具體業(yè)務場景和需求來決定,同時要注意鎖的粒度和持有時間,避免分布式死鎖和性能問題。

關于作者,高宏杰,轉(zhuǎn)轉(zhuǎn)門店技術部研發(fā)工程師。

責任編輯:武曉燕 來源: 轉(zhuǎn)轉(zhuǎn)技術
相關推薦

2013-12-03 13:05:30

Lua腳本語言

2023-10-20 14:08:35

digDNS

2014-06-20 10:51:35

Linux LVM邏輯卷

2011-06-03 08:49:54

Java

2018-02-07 10:59:38

2010-05-26 10:42:20

SVN1.5配置

2023-11-02 14:26:30

PyTorch機器學習

2023-11-02 14:30:25

機器學習

2021-01-05 09:55:46

TmateLinux命令

2009-08-06 17:45:08

C# Webservi

2010-12-15 12:48:26

VirtualBox

2009-09-02 17:38:19

C#開發(fā)GIS

2011-08-17 09:55:45

Objective-CCategory

2021-05-08 09:02:48

KubeBuilderOperatork8s

2009-07-03 13:45:48

JSP簡明教程組件為中心

2013-12-04 11:21:01

2011-08-17 10:00:12

Objective-CProperty

2021-05-11 09:31:31

kustomizeoperator kubernetes

2010-05-25 16:11:25

Git-SVN

2010-01-26 08:25:06

F#語法F#教程
點贊
收藏

51CTO技術棧公眾號