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 在系统里的位置

Pasted image 20260330145258

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 负责简单缓存、字符串、Hash
  • Redisson 负责锁、限流、同步器、分布式集合

这个组合非常常见。


5. 快速开始:Spring Boot 怎么接 Redisson

5.1 Maven 依赖

1
2
3
4
5
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
</dependency>

版本号不要闭眼抄文章。直接对照你当前 Spring Boot / Spring Data Redis 版本看官方兼容说明。

5.2 最简单配置方式

application.yml

1
2
3
4
5
6
spring:
data:
redis:
host: 127.0.0.1
port: 6379
password: 123456

如果你要更完整地控制 Redisson,也可以单独写 redisson.yaml

5.3 自定义配置类

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class RedissonConfig {

@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379")
.setPassword("123456");
return Redisson.create(config);
}
}

5.4 业务里直接注入

1
2
@Resource
private RedissonClient redissonClient;

6. 最核心的能力:分布式锁

这是你最该重点掌握的一章。

6.1 基本使用

1
2
3
4
5
6
7
8
RLock lock = redissonClient.getLock("order:create:lock");

lock.lock();
try {
// 业务逻辑
} finally {
lock.unlock();
}

这个 lock 做了什么?

  • 去 Redis 抢一把名字叫 order:create:lock 的锁
  • 抢到后当前线程进入临界区
  • 其他线程/其他 JVM 只能等
  • unlock() 后别人才能进

6.2 更推荐的写法:tryLock

1
2
3
4
5
6
7
8
9
10
11
12
RLock lock = redissonClient.getLock("voucher:order:" + userId);

boolean isLocked = lock.tryLock(1, 10, TimeUnit.SECONDS);
if (!isLocked) {
throw new IllegalStateException("请求过于频繁,请稍后再试");
}

try {
// 创建订单
} finally {
lock.unlock();
}

这三个参数含义很重要:

  • 第一个 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

Pasted image 20260330145322

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
2
3
4
5
6
7
8
9
10
11
12
13
RLock lock = redissonClient.getLock("myLock");
boolean locked = lock.tryLock(1, 10, TimeUnit.SECONDS);
if (!locked) {
return;
}

try {
// do something
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}

注意这个判断:

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
2
3
RMap<String, Object> map = redissonClient.getMap("user:profile");
map.put("1001", "Gilbert");
Object value = map.get("1001");

它本质上是基于 Redis 的 Map 抽象。

适合:

  • 共享状态
  • 分布式环境下多个实例访问同一份结构化数据

7.2 RMapCache

带过期时间的 Map。

1
2
RMapCache<String, String> mapCache = redissonClient.getMapCache("sms:code");
mapCache.put("13800000000", "9527", 5, TimeUnit.MINUTES);

适合:

  • 验证码
  • 短期状态
  • 带 TTL 的业务数据

7.3 RSet

1
2
3
RSet<Long> set = redissonClient.getSet("signed:users");
set.add(1001L);
boolean exists = set.contains(1001L);

适合:

  • 去重
  • 标签集合
  • 已处理用户集合

7.4 RList / RQueue / RBlockingQueue

1
2
3
RBlockingQueue<String> queue = redissonClient.getBlockingQueue("order:queue");
queue.offer("order-1001");
String msg = queue.take();

适合:

  • JVM 之间共享队列
  • 简单异步削峰
  • 内部任务分发

但这里要明确一件事:

能用不代表就该拿它替代专业 MQ。

后面会讲边界。


7.5 RTopic

1
2
RTopic topic = redissonClient.getTopic("coupon:topic");
topic.publish("new-coupon");

消费者:

1
2
3
4
RTopic topic = redissonClient.getTopic("coupon:topic");
topic.addListener(String.class, (channel, msg) -> {
System.out.println("收到消息: " + msg);
});

适合:

  • 实时通知
  • 配置广播
  • 简单事件传播

但要记住:

  • 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
2
3
4
5
6
RBatch batch = redissonClient.createBatch();
RMapAsync<String, String> map = batch.getMap("user:cache");
map.putAsync("1", "A");
map.putAsync("2", "B");
map.getAsync("1");
BatchResult<?> result = batch.execute();

它的意义是:

  • 减少网络往返
  • 提升一批操作的整体效率

适合:

  • 批量写缓存
  • 批量预热
  • 批量更新状态

但它不是数据库事务的平替。


11. 事务:RTransaction

Redisson 提供事务 API,但你要注意边界:

  • 它解决的是 Redis 侧对象操作的一致性问题
  • 不等于你数据库事务 + Redis 事务天然就一把梭
1
2
3
4
5
RTransaction tx = redissonClient.createTransaction(TransactionOptions.defaults());
RMap<String, String> map = tx.getMap("tx:map");
map.put("k1", "v1");
map.put("k2", "v2");
tx.commit();

你要记住

Redis 事务能力 != 分布式事务万能解。

涉及 MySQL + Redis + MQ 的业务一致性,
别指望 Redisson 的事务 API 一招通杀。


12. Redisson 在真实业务里最常见的几个姿势

12.1 一人一单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
RLock lock = redissonClient.getLock("order:user:" + userId);
boolean success = lock.tryLock(1, 10, TimeUnit.SECONDS);
if (!success) {
throw new RuntimeException("不允许重复下单");
}

try {
// 查询是否已下单
// 扣减库存
// 创建订单
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}

核心思想:

  • 锁的粒度不是“整个业务”
  • 而是 某个用户 / 某个商品 / 某个资源

也就是常说的:

锁要尽量细粒度,别一把大锁锁全场。


12.2 限制某类任务并发数

1
2
3
4
5
6
7
8
9
10
11
12
13
RSemaphore semaphore = redissonClient.getSemaphore("report:generate");
semaphore.trySetPermits(3);

boolean ok = semaphore.tryAcquire(1, 5, TimeUnit.SECONDS);
if (!ok) {
throw new RuntimeException("系统繁忙,请稍后重试");
}

try {
// 生成报表
} finally {
semaphore.release();
}

这比“用锁硬控”更自然。


12.3 多实例共享配置 / 状态

1
2
RMap<String, String> configMap = redissonClient.getMap("app:config");
String value = configMap.get("switchA");

适合:

  • 动态开关
  • 配置共享
  • 小规模共享状态

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/KV
  • Redisson:做分布式锁、信号量、共享集合、同步器
  • MQ:做真正的业务异步消息
  • MySQL 唯一索引:做最终一致性兜底

这个组合比“所有事都硬塞给一种工具”稳定很多。


18. 一页速记版

Redisson 是什么

  • 基于 Redis / Valkey 的 Java 高层客户端
  • 提供分布式对象和服务封装
  • 适合 Java/Spring Boot 工程化开发

核心能力

  • 分布式锁:RLock
  • 公平锁:RFairLock
  • 读写锁:RReadWriteLock
  • 信号量:RSemaphore
  • Map / Set / Queue / Topic
  • 本地缓存:RLocalCachedMap
  • 批处理:RBatch
  • 事务:RTransaction

最常见用途

  • 一人一单
  • 防重复提交
  • 分布式限流/并发控制
  • 多实例共享状态
  • 简单异步任务

最重要的注意点

  • 锁不是数据库唯一约束替代品
  • 锁粒度要细
  • 持锁时间要短
  • Pub/Sub 不等于可靠 MQ
  • 本地缓存不是强一致方案