Redisson
Redisson 详解:从入门到会用,再到知道什么时候别乱用
面向 Java / Spring Boot 后端开发。
这篇不是“把 API 全抄一遍”,而是带你搞清楚:Redisson 是什么、适合什么、核心能力有哪些、分布式锁到底怎么用才不翻车、和 RedisTemplate/Jedis/Lettuce 的关系是什么。
1. Redisson 是什么?
一句话:
Redisson = 一个基于 Redis / Valkey 的 Java 客户端,但它不只是发字符串命令,而是把很多分布式能力封装成了 Java 对象和服务。
你可以把它理解成:
RedisTemplate更像是“你自己写 Redis 命令的适配层”Lettuce/Jedis更像是“原生 Redis client”- Redisson 更像是“把 Redis 变成 Java 分布式工具箱”
它做的事包括但不限于:
- 分布式锁:
RLock/RFairLock/RReadWriteLock/RFencedLock - 分布式集合:
RMap/RSet/RList/RBlockingQueue - 分布式同步器:
RSemaphore/RCountDownLatch - 发布订阅:
RTopic - 本地缓存 + Redis 二级缓存:
RLocalCachedMap - 批处理 / 管道:
RBatch - 事务:
RTransaction - Spring Boot / Spring Cache / Session 等集成
所以它的核心价值不是“连 Redis”,而是:
用更像 Java 的方式使用 Redis 的分布式能力。
2. 一张图先看懂 Redisson 在系统里的位置
3. 为什么很多人一提到 Redis 分布式锁,就会想到 Redisson?
因为它把最容易写炸的部分,帮你封装掉了一大块。
比如你自己手写 Redis 分布式锁,通常要考虑:
SET key value NX EX xxx如何写- value 怎么保证是当前线程唯一标识
- 解锁时怎么保证“只删自己的锁”
- 业务执行时间超出过期时间怎么办
- 宕机后锁会不会永远不释放
- 集群下竞争通知怎么处理
这些点只要你手搓,就非常容易出现 subtle bug。
Redisson 的 RLock 已经把很多这些东西处理好了,尤其是:
- 可重入
- 支持等待获取锁
- 支持 leaseTime 自动释放
- 支持 watchdog 续期
- 只允许持有锁的线程解锁
所以从工程角度看:
不是说你不能自己写锁,
而是 Redisson 通常更稳、更省脑子、更适合业务代码维护。
4. Redisson 和 RedisTemplate / Jedis / Lettuce 到底什么关系?
| 对比项 | RedisTemplate | Jedis / Lettuce | Redisson |
|---|---|---|---|
| 定位 | Spring 对 Redis 的操作模板 | 原生 Redis 客户端 | 高层封装的 Redis 分布式工具箱 |
| 操作风格 | 偏命令式 | 更接近原生命令 | 偏对象式 / 组件式 |
| 分布式锁 | 需要自己实现 | 需要自己实现 | 开箱即用 |
| 集合抽象 | 基础支持 | 原生命令级 | 丰富分布式对象 |
| 发布订阅 | 能做 | 能做 | 更面向 Java 对象 |
| Spring 集成 | 强 | 一般 | 强 |
| 适用场景 | 常规缓存 / KV 操作 | 精细控制 Redis 命令 | 分布式锁、同步器、对象封装 |
怎么选?
实际项目里很常见的是:
- 简单 KV / 缓存读写:RedisTemplate 就够了
- 需要分布式锁 / 分布式同步器 / 高层对象:上 Redisson
- 两者并存:完全正常
很多项目不是“只能二选一”,而是:
RedisTemplate负责简单缓存、字符串、HashRedisson负责锁、限流、同步器、分布式集合
这个组合非常常见。
5. 快速开始:Spring Boot 怎么接 Redisson
5.1 Maven 依赖
1 | <dependency> |
版本号不要闭眼抄文章。直接对照你当前 Spring Boot / Spring Data Redis 版本看官方兼容说明。
5.2 最简单配置方式
application.yml
1 | spring: |
如果你要更完整地控制 Redisson,也可以单独写 redisson.yaml。
5.3 自定义配置类
1 |
|
5.4 业务里直接注入
1 |
|
6. 最核心的能力:分布式锁
这是你最该重点掌握的一章。
6.1 基本使用
1 | RLock lock = redissonClient.getLock("order:create:lock"); |
这个 lock 做了什么?
- 去 Redis 抢一把名字叫
order:create:lock的锁 - 抢到后当前线程进入临界区
- 其他线程/其他 JVM 只能等
unlock()后别人才能进
6.2 更推荐的写法:tryLock
1 | RLock lock = redissonClient.getLock("voucher:order:" + userId); |
这三个参数含义很重要:
- 第一个
1:最多等 1 秒 - 第二个
10:拿到锁后,10 秒自动释放 - 第三个:时间单位
为什么推荐 tryLock?
因为业务上你通常不希望:
- 无限阻塞
- 锁一直等
- 用户请求卡死
在 Web 场景下,能快速失败,往往比一直等更合理。
6.3 watchdog 是什么?
这是 Redisson 的招牌能力之一。
问题背景
假设你只给锁设置了 10 秒过期时间:
- 线程 A 拿到锁
- 业务执行了 15 秒
- 10 秒一到,锁自动过期
- 线程 B 又拿到锁
- 这时 A 和 B 可能同时执行临界区
这就炸了。
Redisson 的处理
如果你调用的是:
1 | lock.lock(); |
而不是显式传 leaseTime,Redisson 会启用 watchdog 看门狗机制:
- 默认锁超时是 30 秒
- 只要持锁客户端还活着
- Redisson 就会持续给这把锁续期
- 业务结束后正常
unlock()
你可以这样理解
leaseTime是“写死一个固定过期时间”watchdog是“只要我还活着,就别让锁过期”
6.4 一张图看懂锁 + watchdog
6.5 lock.lock() 和 lock.lock(10, TimeUnit.SECONDS) 的区别
写法 1
1 | lock.lock(); |
特点:
- 没指定固定过期时间
- 走 watchdog 自动续期
- 适合 执行时间不稳定 的业务
写法 2
1 | lock.lock(10, TimeUnit.SECONDS); |
特点:
- 明确 10 秒后自动释放
- 不会依赖 watchdog 一直续命
- 适合 业务耗时可预测 的场景
经验结论
- 耗时不稳定的业务:更适合
lock()/tryLock(waitTime, leaseTime, unit)谨慎设计 - 耗时稳定且短:可以显式给
leaseTime - 你根本估不准耗时:别随手写一个很小的
leaseTime
6.6 为什么一定要 finally unlock()?
因为不管是正常返回、异常返回、还是中途抛错,
只要你拿了锁,释放动作都必须保证执行。
标准模板:
1 | RLock lock = redissonClient.getLock("myLock"); |
注意这个判断:
1 | lock.isHeldByCurrentThread() |
它能避免一些异常情况下误解锁。
6.7 常见锁类型
1)RLock
最常用,普通可重入分布式锁。
适合:
- 一人一单
- 同一资源串行处理
- 避免并发重复提交
2)RFairLock
公平锁,按请求顺序拿锁。
适合:
- 对“先来先得”有要求
- 但通常性能会差一点,不要无脑上
3)RReadWriteLock
读写锁:
- 读可以并发
- 写必须独占
适合:
- 读多写少
- 需要更细粒度并发控制
4)RSemaphore
信号量,不是“只有一个线程进”的锁,而是“允许 N 个同时进”。
适合:
- 限流
- 控制并发数
- 连接池位点控制
5)RFencedLock
这个偏 advanced,但非常值得知道。
它会返回一个 fencing token,适合处理:
- GC pause
- 网络抖动
- 客户端虽然“以为自己还持锁”,但实际上已经失去所有权
如果你的下游资源支持校验 token,RFencedLock 比普通锁更安全。
7. Redisson 不只有锁:它其实还有很多“分布式 Java 对象”
7.1 RMap
1 | RMap<String, Object> map = redissonClient.getMap("user:profile"); |
它本质上是基于 Redis 的 Map 抽象。
适合:
- 共享状态
- 分布式环境下多个实例访问同一份结构化数据
7.2 RMapCache
带过期时间的 Map。
1 | RMapCache<String, String> mapCache = redissonClient.getMapCache("sms:code"); |
适合:
- 验证码
- 短期状态
- 带 TTL 的业务数据
7.3 RSet
1 | RSet<Long> set = redissonClient.getSet("signed:users"); |
适合:
- 去重
- 标签集合
- 已处理用户集合
7.4 RList / RQueue / RBlockingQueue
1 | RBlockingQueue<String> queue = redissonClient.getBlockingQueue("order:queue"); |
适合:
- JVM 之间共享队列
- 简单异步削峰
- 内部任务分发
但这里要明确一件事:
能用不代表就该拿它替代专业 MQ。
后面会讲边界。
7.5 RTopic
1 | RTopic topic = redissonClient.getTopic("coupon:topic"); |
消费者:
1 | RTopic topic = redissonClient.getTopic("coupon:topic"); |
适合:
- 实时通知
- 配置广播
- 简单事件传播
但要记住:
- Pub/Sub 类模型天生不擅长消息持久化
- 掉线期间的消息,可能拿不到
所以它不是 RabbitMQ / Kafka 的平替。
8. Redisson 适不适合拿来做消息队列?
可以做什么
- 轻量级异步任务
- 低复杂度内部通知
- 对可靠性要求不极端的削峰
不太适合什么
- 复杂消费确认
- 长时间堆积的大消息流
- 强顺序、大吞吐、复杂重试
- 完整死信队列 / 回溯 / 多消费组治理
工程上的判断标准
如果你要的是:
- 真正的业务消息系统
- 高可靠投递
- 消费失败重试
- 死信、延迟、积压治理
那就优先上 RabbitMQ / Kafka / RocketMQ 这类专业 MQ。
如果你要的是:
- 一个项目内的轻量异步能力
- 简单共享队列
- 配合 Redis 现有基础设施快速实现
那 Redisson 的队列能力可以用。
9. 本地缓存:RLocalCachedMap
这是 Redisson 很有意思的一块。
它的思路是:
- 数据主存仍然在 Redis
- 每个应用实例本地再维护一份 near cache
- 读请求先走本地缓存
- 更新时通过 pub/sub 做失效通知或同步
好处
- 降低 Redis 压力
- 减少网络往返
- 热点读性能会非常好
风险
- 本地缓存一致性复杂度更高
- 适合“最终一致”接受度较高的读多写少场景
适合:
- 配置数据
- 热点字典
- 变化不频繁的只读元数据
不适合:
- 强一致要求很高的核心交易数据
10. 批处理:RBatch
如果你要一次发很多 Redis 命令,Redisson 支持批处理。
1 | RBatch batch = redissonClient.createBatch(); |
它的意义是:
- 减少网络往返
- 提升一批操作的整体效率
适合:
- 批量写缓存
- 批量预热
- 批量更新状态
但它不是数据库事务的平替。
11. 事务:RTransaction
Redisson 提供事务 API,但你要注意边界:
- 它解决的是 Redis 侧对象操作的一致性问题
- 不等于你数据库事务 + Redis 事务天然就一把梭
1 | RTransaction tx = redissonClient.createTransaction(TransactionOptions.defaults()); |
你要记住
Redis 事务能力 != 分布式事务万能解。
涉及 MySQL + Redis + MQ 的业务一致性,
别指望 Redisson 的事务 API 一招通杀。
12. Redisson 在真实业务里最常见的几个姿势
12.1 一人一单
1 | RLock lock = redissonClient.getLock("order:user:" + userId); |
核心思想:
- 锁的粒度不是“整个业务”
- 而是 某个用户 / 某个商品 / 某个资源
也就是常说的:
锁要尽量细粒度,别一把大锁锁全场。
12.2 限制某类任务并发数
1 | RSemaphore semaphore = redissonClient.getSemaphore("report:generate"); |
这比“用锁硬控”更自然。
12.3 多实例共享配置 / 状态
1 | RMap<String, String> configMap = redissonClient.getMap("app:config"); |
适合:
- 动态开关
- 配置共享
- 小规模共享状态
13. 常见坑,真的很重要
13.1 不要把 Redisson 锁当数据库唯一约束替代品
Redisson 锁能降低并发冲突概率,
但数据库唯一索引 / 唯一约束仍然是最后一道防线。
比如一人一单:
- 应用层:用 Redisson 锁减少并发冲突
- 数据层:用唯一索引兜底
这是更稳的做法。
13.2 锁粒度不要太粗
错误示例:
1 | redissonClient.getLock("create-order-lock") |
这相当于全站创建订单串行化,直接把吞吐打没。
更合理:
1 | redissonClient.getLock("create-order-lock:" + userId) |
或者:
1 | redissonClient.getLock("stock-lock:" + voucherId) |
13.3 不要在持锁期间做太慢的操作
比如:
- 调第三方接口
- 大文件 IO
- 超慢 SQL
- 复杂远程调用
持锁时间越长:
- 吞吐越低
- 冲突越多
- 风险越大
思路永远是:
缩小临界区。把真正需要互斥的那几行逻辑圈出来。
13.4 unlock() 不是谁都能调
Redisson 的 RLock 遵循 Java Lock 语义:
- 只有持有锁的线程才能解锁
- 否则会抛
IllegalMonitorStateException
所以异步线程、线程切换、跨线程释放锁这类场景,
要格外小心。
13.5 Pub/Sub 不等于可靠消息
RTopic 很方便,
但别把它理解成“带持久化确认重试的 MQ”。
这两者不是一个层级的东西。
13.6 本地缓存不是银弹
near cache 很爽,
但你必须想清楚:
- 数据一致性能不能接受延迟
- 热点键变化是否频繁
- 多实例更新通知是否足够稳定
如果你要的是强一致读,
那就别把本地缓存当万能性能药。
14. 什么时候该用 Redisson,什么时候别硬上?
适合用 Redisson
- Spring Boot + Redis 项目
- 需要分布式锁
- 需要分布式同步器
- 需要共享集合 / 共享状态
- 需要简单异步队列 / 广播通知
- 想减少手写 Redis Lua / 锁逻辑
不适合硬上 Redisson 的情况
- 只是简单缓存读写
- 只是 get / set / incr / expire 这种基础命令
- 对消息系统要求已经接近专业 MQ
- 对数据一致性和吞吐要求极高,但又没有想清楚 Redis 边界
一句话:
Redisson 强在“分布式能力封装”,不是强在“什么都替你做”。
15. 面试回答模板:什么是 Redisson?
你可以这样答:
Redisson 是一个基于 Redis 的 Java 客户端,但相比 Jedis/Lettuce 这种更偏底层的 client,它提供了更高层的分布式对象和服务封装,比如分布式锁、读写锁、信号量、Map、Set、阻塞队列、发布订阅、本地缓存等。
在 Spring Boot 项目里,它特别适合做分布式锁和分布式同步控制。
它的一个典型优势是把锁的可重入、自动续期、等待获取、自动释放等能力封装好了,比手写 Redis 分布式锁更稳。
但它也不是万能的,比如消息系统复杂度高时还是应该用专业 MQ,数据库唯一约束也不能被锁替代。
16. 面试回答模板:Redisson 锁为什么比自己手搓更常用?
你可以这样答:
因为自己手写 Redis 分布式锁,往往只会写到 setnx + expire,但细节很多,比如可重入、误删别人的锁、业务超时、锁续期、线程所有权校验、集群下通知唤醒等,这些点很容易出 bug。
Redisson 把这些常见问题抽象成了RLock等对象,还支持 watchdog 自动续期,所以在 Java 工程里更常见,也更适合维护。
17. 一份更工程化的使用建议
给 Gilbert 一个比较实战的建议:
在项目里这样分工很舒服
RedisTemplate:做基础缓存、计数器、简单 Hash/KVRedisson:做分布式锁、信号量、共享集合、同步器MQ:做真正的业务异步消息MySQL 唯一索引:做最终一致性兜底
这个组合比“所有事都硬塞给一种工具”稳定很多。
18. 一页速记版
Redisson 是什么
- 基于 Redis / Valkey 的 Java 高层客户端
- 提供分布式对象和服务封装
- 适合 Java/Spring Boot 工程化开发
核心能力
- 分布式锁:
RLock - 公平锁:
RFairLock - 读写锁:
RReadWriteLock - 信号量:
RSemaphore - Map / Set / Queue / Topic
- 本地缓存:
RLocalCachedMap - 批处理:
RBatch - 事务:
RTransaction
最常见用途
- 一人一单
- 防重复提交
- 分布式限流/并发控制
- 多实例共享状态
- 简单异步任务
最重要的注意点
- 锁不是数据库唯一约束替代品
- 锁粒度要细
- 持锁时间要短
- Pub/Sub 不等于可靠 MQ
- 本地缓存不是强一致方案