Redisson简明教程—你家的锁芯该换了

news2025/6/7 0:42:25

1.简介

各位攻城狮们,你还在使用原生命令来上锁么?看来你还是不够懒,饺子都给你包好了,你非要吃大饼配炒韭菜,快点改善一下“伙食”吧,写代码也要来点幸福感。今天咱们就来聊聊Redisson提供的各种锁,Redisson就像是Redis给Java程序员的一把瑞士军刀,不仅能存数据,还能玩出各种分布式花样。

  • Redis版本:Redis 2.8+,理想版本5.0+(支持 Stream、模块化等高级特性,Redisson 能秀出全部技能)。

  • 架构模式:支持单机、哨兵和集群(集群模式可靠性更高)

2.看门狗

想象你上厕所(获取锁)时带着一只忠心耿耿的阿黄(看门狗)。当你蹲坑时间快到时(锁快要过期),阿黄就会大叫:"主人你还没完事吗?我给你续时间啦!"(自动续期)

2-1.为什么需要这个"狗子"?

  • 防止业务没执行完锁就过期:默认锁30秒过期,但万一你的业务要31秒呢?

  • 避免锁丢失:如果客户端崩溃,看门狗停止续期,锁最终会自动释放

  • 不用手动计算业务时间:再也不用战战兢兢估算业务执行时间了

2-2.工作原理

图片

  • 首次加锁:默认设置锁过期时间30秒

  • 启动看门狗:加锁成功后启动一个定时任务(后台线程)

  • 定期续期:每10秒(过期时间的1/3)检查业务是否完成。未完成:执行expire命令把锁再续30秒;已完成:停止续期。

  • 最终释放:业务完成调用unlock或客户端断开连接时释放

2-3.如何“撸狗子”

Config config = new Config();
config.setLockWatchdogTimeout(30000L); // 单位毫秒,默认就是30秒
// ...
RedissonClient redisson = Redisson.create(config);
// 你也可以在加锁时指定(会覆盖默认值)
lock.lock(60, TimeUnit.SECONDS); // 这时看门狗会按60秒周期续期

3.可重入锁:你的分布式"万能钥匙"

我们都知道Java中ReentrantLock和synchronized都是可重入锁,但都只能用于单机环境的,在分布式环境下,Redisson给我提供了类似体验的可重入锁。

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

  • 线程安全:不同JVM的相同线程也可重入

  • 自动续期:看门狗机制保活(默认30秒)

  • 公平/非公平:两种模式可选

  • 超时机制:避免无限等待

3-2.使用姿势大全

基础款(阻塞式):
RLock lock = redisson.getLock("orderLock");
lock.lock(); // 一直等到天荒地老
try {
    // 你的核心业务
} finally {
    lock.unlock(); // 一定要放在finally!
}
高级款(尝试获取):
if (lock.tryLock(3, 30, TimeUnit.SECONDS)) { // 最多等3秒,锁30秒自动过期
    try {
        // 业务处理
    } finally {
        lock.unlock();
    }
} else {
    log.warn("获取锁失败,换个姿势再试一次");
}
骚操作款(异步获取):
RFuture<Void> lockFuture = lock.lockAsync();
lockFuture.whenComplete((res, ex) -> {
    if (ex == null) {
        try {
            // 异步业务处理
        } finally {
            lock.unlock();
        }
    }
});

3-3.公平锁

  • 排队机制:使用Redis的List结构维护等待队列

  • 订阅发布:通过Redis的pub/sub通知下一个等待者

  • 双重检查:获取锁时检查自己是否在队列头部

RLock fairLock = redisson.getFairLock("myFairLock");
try {
    fairLock.lock();
    // 业务逻辑
} finally {
    fairLock.unlock();
}

3-4.非公平锁

  • 直接竞争:所有线程同时尝试CAS操作

  • 效率优先:没有队列维护开销

  • 可能饥饿:运气差的线程可能长期得不到锁

RLock nonFairLock = redisson.getLock("hotItemLock");
if (nonFairLock.tryLock(50, TimeUnit.MILLISECONDS)) { // 拼手速!
    try {
        // 秒杀业务逻辑
    } finally {
        nonFairLock.unlock();
    }
}

3-5.可重入原理深扒

加锁Lua脚本伪代码:
-- 参数:锁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
    -- 重入情况:计数器+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脚本伪代码:
-- 参数:锁key、客户端ID+线程ID
if (redis.call('hexists', KEYS[1], ARGV[1]) == 0) then
    -- 压根没持有锁
    returnnil;
end;

-- 重入次数-1
local counter = redis.call('hincrby', KEYS[1], ARGV[1], -1);
if (counter > 0) then
    -- 还有重入次数,更新过期时间
    redis.call('pexpire', KEYS[1], 30000);
    return0;
else
    -- 最后一次解锁,删除key
    redis.call('del', KEYS[1]);
    -- 发布解锁消息
    redis.call('publish', KEYS[2], ARGV[2]);
    return1;
end;

4.联锁:分布式锁中的"全家桶套餐"

4-1.联锁是什么?——"要么全有,要么全无"的霸道总裁

想象你要同时约三个女神约会:

  • 女神A:周末有空 ✅

  • 女神B:周末有空 ✅

  • 女神C:周末要加班 ❌ Redisson联锁的做法是:只要有一个拒绝,就取消所有约会!这就是联锁的"All or Nothing"哲学。

4-2.为什么需要联锁?

典型场景:
  • 跨资源事务:需要同时锁定订单、库存、优惠券三个系统

  • 数据一致性:确保多个关联资源同时被保护

  • 避免死锁:防止交叉等待导致的死锁情况

4-3.联锁使用三件套

基本用法:
// 准备三把锁(就像三个女神的联系方式)
RLock lock1 = redisson.getLock("order_lock");
RLock lock2 = redisson.getLock("stock_lock");
RLock lock3 = redisson.getLock("coupon_lock");

// 创建联锁"约会套餐"
RedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2, lock3);

try {
    // 尝试同时锁定(约三位女神)
    if (multiLock.tryLock(3, 30, TimeUnit.SECONDS)) {
        // 三位都同意了!开始你的表演
        processOrder();
        updateStock();
        useCoupon();
    } else {
        log.warn("有个女神拒绝了你");
    }
} finally {
    multiLock.unlock(); // 记得送她们回家
}
高阶技巧:
// 动态构造联锁(适合不确定数量的资源)
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.联锁的硬核原理

加锁流程:
  • 顺序加锁:按传入锁的顺序依次尝试获取

  • 失败回滚:任意一个锁获取失败时,释放已获得的所有锁

  • 统一过期时间:所有锁使用相同的过期时间

底层Lua脚本(简化版):
-- 参数:多个锁的KEYS,统一过期时间,线程标识
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
    -- 释放已经获取的锁
    for j = 1, i-1do
        redis.call('del', KEYS[j])
    end
    return0
end
return1

4-5.联锁的三大禁忌

乱序使用(导致死锁):
// 线程1:
multiLock(lockA, lockB).lock();

// 线程2:
multiLock(lockB, lockA).lock(); // 危险!可能死锁

✅ 正确做法:全局统一加锁顺序

混合锁类型:
// 混合普通锁和公平锁
new MultiLock(lock1, fairLock2); // 不推荐

✅ 正确做法:使用相同特性的锁组合

忽略部分锁失败:
if (!multiLock.tryLock()) {
    // 直接返回,不处理部分获取成功的情况
    return; // 危险!
}

✅ 正确做法:确保完全获取或完全失败

5.Redisson读写锁:分布式系统中的"读写分离"高手

也许单机版的ReentrantReadWriteLock你听说过,但是分布式环境下的版本可能很少接触到。

典型场景:
  • 读多写少系统:比如商品详情页(每秒上万次读取,每分钟几次更新)

  • 数据一致性要求:保证读取时不会读到半成品数据

  • 系统性能优化:避免读操作被不必要的串行化

5-1.使用姿势

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

// 读操作(多个线程可同时进入)
rwLock.readLock().lock();
try {
    // 查询数据(安全读取)
    Book book = getBookFromDB(123);
} finally {
    rwLock.readLock().unlock();
}

// 写操作(独占访问)
rwLock.writeLock().lock();
try {
    // 修改数据(安全写入)
    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;

-- 增加读锁计数
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):分布式系统中的"限量入场券"

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

6-1.使用姿势大全

基础用法:
// 获取信号量(初始10个许可)
RSemaphore semaphore = redisson.getSemaphore("apiLimit");
semaphore.trySetPermits(10); // 设置许可数量

// 获取许可(阻塞直到可用)
semaphore.acquire();
try {
    // 执行业务(保证最多10个并发)
    callLimitedAPI();
} finally {
    semaphore.release(); // 记得归还!
}

// 尝试获取(非阻塞)
if (semaphore.tryAcquire()) {
    try {
        // 抢到许可了!
    } finally {
        semaphore.release();
    }
} else {
    log.warn("系统繁忙,请稍后再试");
}
高级技巧:
// 带超时的尝试获取
if (semaphore.tryAcquire(3, 500, TimeUnit.MILLISECONDS)) {
    try {
        // 在500ms内获取到3个许可
        batchProcess();
    } finally {
        semaphore.release(3); // 归还多个许可
    }
}

// 动态调整许可数量
semaphore.addPermits(5); // 增加5个许可(扩容)
semaphore.reducePermits(3); // 减少3个许可(缩容)

6-2.原理深扒

获取许可的Lua脚本(简化):
-- 参数:信号量key、请求许可数
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脚本(简化):
-- 参数:信号量key、释放许可数
return redis.call('incrby', KEYS[1], ARGV[1])

7.红锁(RedLock):分布式锁界的“联合国维和部队”

RedLock 的诞生是为了对抗单点 Redis 挂掉后锁失效的问题。它的目标就是:“即使部分节点挂了,我也要稳如老狗”。

7-1.核心原理

Redisson 的 RedLock 实现来源于 Redis 作者 antirez 提出的 Redlock 算法。流程如下:

  • 准备多个独立的 Redis 节点(注意:是互相独立的,不是主从复制结构)。

  • 客户端依次向这些节点尝试加锁(使用 SET NX PX 命令)。

  • 记录耗时:加锁操作总共不能超过锁过期时间的 1/2(比如设置锁有效期 10 秒,那就必须 5 秒内搞定加锁)。

  • 加锁成功节点超过半数(N/2 + 1)视为成功。

  • 若失败,立刻释放所有加锁成功的节点,以避免资源死锁。

  • 释放锁时,同样要向所有节点发送 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");

// 构造红锁(建议奇数个,通常3/5个)
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);

try {
    // 尝试获取锁(等待时间100s,锁持有时间30s)
    boolean locked = redLock.tryLock(100, 30, TimeUnit.SECONDS);
    if (locked) {
        // 执行业务逻辑
        doCriticalWork();
    }
} finally {
    redLock.unlock();
}

8.闭锁(CountDownLatch):分布式系统中的"集结号"

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

8-1.使用姿势

// 主协调节点(教官)
RCountDownLatch latch = redisson.getCountDownLatch("batchTaskLatch");
latch.trySetCount(5); // 需要等待5个任务

// 工作节点(学员)
RCountDownLatch workerLatch = redisson.getCountDownLatch("batchTaskLatch");
workerLatch.countDown(); // 完成任务时调用

// 主节点等待(在另一个线程/JVM)
latch.await(); // 阻塞直到计数器归零
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]) -- 清理计数器
end
return remaining

-- await操作
local count = redis.call('get', KEYS[1])
if count == falseortonumber(count) <= 0then
    return1-- 已经完成
end
return0-- 需要继续等待

9.总结

Redisson 提供了丰富的分布式锁实现,适用于各种分布式场景,使用体验更好,选择锁类型时应根据具体业务场景和需求来决定,同时要注意锁的粒度和持有时间,避免分布式死锁和性能问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2402294.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

48V带极性反接保护-差共模浪涌防护方案

在工业自动化&#xff08;电动机驱动 / 工业机器人&#xff09;、交通基础设施&#xff08;充电桩 / 车载电子&#xff09;、安防系统&#xff08;监控摄像头 / 门禁&#xff09;、储能设备&#xff08;BMS / 离网控制器&#xff09;等领域&#xff0c;DC48V 电源因安全特低电压…

Python----目标检测(使用YOLO 模型进行线程安全推理和流媒体源)

一、线程安全推理 在多线程环境中运行YOLO 模型需要仔细考虑&#xff0c;以确保线程安全。Pythons threading 模块允许您同时运行多个线程&#xff0c;但在这些线程中使用YOLO 模型时&#xff0c;需要注意一些重要的安全问题。本页将指导您创建线程安全的YOLO 模型推理。 1.1、…

jvm学习第1day jvm简介,栈溢出、堆溢出

jvm学习第1day jvm简介&#xff0c;栈溢出、堆溢出 jvm简介栈线程安全栈溢出线程运行诊断堆堆溢出 方法区方法区内存溢出常量池和运行时常量池 jvm简介 jvm 是编译后的字节码文件运行的环境&#xff0c; 因此各个平台有了jvm可以运行java.class文件&#xff0c;这是Java跨平台…

用广告维持的免费 AI 图像生成工具(个人项目分享)

用广告维持的免费 AI 图像生成工具&#xff08;个人项目分享&#xff09; 免费 AI 图像生成工具网址&#xff1a;https://aiart.gcc.ac.cn/ 最近做了一个 AI 图像生成器&#xff0c;主要目标是“尽量简单”&#xff1a; 打开网页就能用不用注册、不用登录免费&#xff0c;不…

分析Web3下数据保护的创新模式

在这个信息爆炸的时代&#xff0c;我们正站在 Web3 的门槛上&#xff0c;迎接一个以去中心化、用户主权和数据隐私为核心的新时代。Web3 不仅仅是技术的迭代&#xff0c;它更是一场关于数据权利和责任的结构性变革。本文将探讨 Web3 下数据保护的创新模式&#xff0c;以期为用户…

​减少交通拥堵、提高效率、改善交通安全的智慧交通开源了。

智慧交通视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。用户只需在界面上…

协议融合驱动效能跃升:Modbus转Ethernet IP的挤出吹塑机应用

在现代工业自动化领域&#xff0c;Modbus作为一种串行通信协议&#xff0c;其稳定性和简单性被广泛应用于各种工控设备中。但随着技术的进步&#xff0c;对于更高速、更远传输距离的需求日益增长&#xff0c;这就需要将Modbus协议通过以太网进行传输&#xff0c;即实现Modbus T…

bug 记录 - 使用 el-dialog 的 before-close 的坑

需求说明 弹窗中内嵌一个 form 表单 原始代码 <script setup lang"ts"> import { reactive, ref } from "vue" import type { FormRules } from element-plus const ruleFormRef ref() interface RuleForm {name: stringregion: number | null } …

Next.js 中间件鉴权绕过漏洞 CVE-2025-29927

前言:CVE-2025-29927 是一个影响 Next.js 的严重漏洞&#xff0c;源于开发者信任了客户端请求中携带的 X-Middleware-Rewrite 头部字段。攻击者可以手动构造该头部&#xff0c;实现绕过中间件逻辑&#xff0c;访问本应受保护的资源或 API。 影响版本&#xff1a;Next.js < …

基于YOLO-NAS-Pose的无人机象群姿态估计:群体行为分析的突破

【导读】 应对气候变化对非洲象的生存威胁&#xff0c;本研究创新采用无人机航拍结合AI姿态分析技术&#xff0c;突破传统观测局限。团队在肯尼亚桑布鲁保护区对比测试DeepLabCut与YOLO-NAS-Pose两种模型&#xff0c;首次将后者引入野生动物研究。通过检测象群头部、脊柱等关键…

8天Python从入门到精通【itheima】-71~72(数据容器“序列”+案例练习)

目录 71节-数据容器“序列”的切片 1.学习目标 2.什么是序列 3.序列的常用操作——切片 4.小节总结 72节——案例练习&#xff1a;序列的切片实践 1.案例需求 2.代码实战 好了&#xff0c;又一篇博客和代码写完了&#xff0c;励志一下吧&#xff0c;下一小节等等继续&a…

dvwa10——XSS(DOM)

XSS攻击&#xff1a; DOM型XSS 只在浏览器前端攻击触发&#xff1a;修改url片段代码不存储 反射型XSS 经过服务器攻击触发&#xff1a;可能通过提交恶意表单&#xff0c;连接触发代码不存储 存储型XSS 经由服务器攻击触发&#xff1a;可能通过提交恶意表单&#xff0c;连…

dvwa14——JavaScript

LOW 先按提示尝试输入success&#xff0c;提交失败 那用bp抓包一下 &#xff0c;抓到这些&#xff0c;发现有token验证&#xff0c;说明改对token才能过 返回页面f12看一下源码&#xff0c;发现value后面的值像密码&#xff0c;于是试一下md5和rot13的解密 ROT13加密/解密 - …

机器学习实验八--基于pca的人脸识别

基于pca的人脸识别 引言&#xff1a;pca1.pca是什么2.PCA算法的基本步骤 实例&#xff1a;人脸识别1.实验目的2.实现步骤3.代码实现4.实验结果5.实验总结 引言&#xff1a;pca 1.pca是什么 pca是一种统计方法&#xff0c;它可以通过正交变换将一组可能相关的变量转换成一组线…

LabVIEW的AMC架构解析

此LabVIEW 程序基于消息队列&#xff08;Message Queue&#xff09;机制构建 AMC 架构&#xff0c;核心包含消息生成&#xff08;MessageGenerator &#xff09;与消息处理&#xff08;Message Processor &#xff09;两大循环&#xff0c;通过队列传递事件与指令&#xff0c;实…

MySQL 索引:为使用 B+树作为索引数据结构,而非 B树、哈希表或二叉树?

在数据库的世界里&#xff0c;性能是永恒的追求。而索引&#xff0c;作为提升查询速度的利器&#xff0c;其底层数据结构的选择至关重要。如果你深入了解过 MySQL&#xff08;尤其是其主流存储引擎 InnoDB&#xff09;&#xff0c;你会发现它不约而同地选择了 B树 作为索引的主…

ubuntu屏幕复制

在ubnuntu20中没有办法正常使用镜像功能,这里提供一下复制屏幕的操作. 使用xrandr查看所有的显示器情况 这里我发现自己的电脑没有办法直接设置分辨率,但是外接的显示器可以设置,从命令行来说就是设置: xrandr --output HDMI-0 --mode 1920x1080那怎么样才能将原生电脑屏幕换…

Spring WebFlux 整合AI大模型实现流式输出

前言 最近赶上AI的热潮&#xff0c;很多业务都在接入AI大模型相关的接口去方便的实现一些功能&#xff0c;后端需要做的是接入AI模型接口&#xff0c;并整合成流式输出到前端&#xff0c;下面有一些经验和踩过的坑。 集成 Spring WebFlux是全新的Reactive Web技术栈&#xf…

验证电机理论与性能:电机试验平板提升测试效率

电机试验平板提升测试效率是验证电机理论与性能的重要环节之一。通过在平板上进行电机试验&#xff0c;可以对电机的性能参数进行准确测量和分析&#xff0c;从而验证电机的理论设计是否符合实际表现。同时&#xff0c;提升测试效率可以加快试验过程&#xff0c;节约时间和成本…

Simplicity studio SDK下载和安装,创建工程

下载SDK工具地址 Simplicity Studio - Silicon Labs 选择适合自己电脑的版本。 这个就使用你自己的邮箱注册一个就可以了&#xff0c;我是用的公司邮箱注册的。 下载完成&#xff1a; 安装 下载完成后右键点击安装&#xff0c;一路下一步 安装完成后&#xff0c;程序自动打…