核心API

Redisson 最值得掌握的 API,可以按用途分成以下几类。

业务场景 核心 API 典型用途
普通缓存、对象存储 RBucket 缓存用户信息、开关配置、幂等标记
Hash、带过期时间的 Map RMap、RMapCache 用户属性、分布式缓存
Set、List、排序集合 RSet、RList、RScoredSortedSet 去重、列表、排行榜
原子计数 RAtomicLong 计数器、序号
分布式锁 RLock 防止并发重复操作
公平锁、读写锁 RFairLock、RReadWriteLock FIFO 加锁、读写互斥
信号量 RSemaphore 限制同时执行的任务数
分布式限流 RRateLimiter 接口限流、控制调用频率
队列 RQueue、RBlockingQueue 等 任务传递、生产者消费者
发布订阅 RTopic 消息通知、事件广播
分布式计数与同步 RCountDownLatch 多个任务汇合、等待初始化

这里有一个重要区别:分布式数据结构解决数据共享问题,分布式同步器解决多个应用实例之间的并发协调问题。 例如,RMap 适合保存共享数据,而 RLock 适合协调多个服务实例对同一业务资源的操作。

二、引入依赖与连接 Redis

2.1 Maven 依赖

如果只想使用 Redisson 核心 API,推荐先引入 redisson,自己管理客户端 Bean,适合学习以及需要精确控制 Redis 连接的 Spring Boot 项目。

1
2
3
4
5
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.27.2</version>
</dependency>

这里用的是 Redisson 3.x 的示例版本。实际项目应根据 Java、Spring Boot、Spring Data Redis 版本选择兼容版本,不要直接把最新版 starter 塞进旧项目。Redisson 官方将其 Spring Data 适配模块分别对应 Spring Boot 2.x、3.x 和 4.x。

如果希望 Spring Boot 自动装配,可以使用 redisson-spring-boot-starter,但要注意其版本与项目现有依赖的兼容性。

2.2 配置 RedissonClient

最基础也最方便调试的方式是手动创建客户端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {

@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() {
Config config = new Config();

config.useSingleServer()
.setAddress("redis://127.0.0.1:6379")
// Redis 没有密码时删除下一行
.setPassword("your-password")
.setDatabase(0)
.setConnectionMinimumIdleSize(8)
.setConnectionPoolSize(32)
.setConnectTimeout(10000)
.setTimeout(3000);

config.setThreads(16);
config.setNettyThreads(32);

return Redisson.create(config);
}
}

上面的线程数和连接池大小只是示例,应根据业务的并发量、Redis 实例规格和压测结果调整。

这里的关键是:

  • RedissonClient 应当作为单例 Bean 复用,不要每个请求创建一个客户端。
  • shutdown() 应当在应用关闭时调用。
  • redis:// 表示普通 Redis 连接,启用 TLS 时通常使用 rediss://。
  • setDatabase(0) 指定 Redis 逻辑数据库,Redis Cluster 场景不能照搬多数据库的配置思路。

Redisson 支持单机、主从、Sentinel 和 Cluster 等部署方式。

2.3 Redis Cluster 配置

如果 Redis 使用集群模式,需要使用 useClusterServers(),而不是 useSingleServer()。

1
2
3
4
5
6
7
8
9
10
11
12
13
Config config = new Config();

config.useClusterServers()
.addNodeAddress(
"redis://10.0.0.1:6379",
"redis://10.0.0.2:6379",
"redis://10.0.0.3:6379"
)
.setPassword("your-password")
.setConnectTimeout(10000)
.setTimeout(3000);

RedissonClient client = Redisson.create(config);

实际部署时,节点地址、密码以及 TLS 配置需要使用环境配置或密钥管理系统注入,不要硬编码在代码仓库。

三、基本数据类型与集合操作

3.1 RBucket:操作普通对象

RBucket<T> 可以理解为 Redis 中一个能够存放序列化 Java 对象的值容器。它适合普通缓存、配置、标记和单值数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.redisson.api.RBucket;
import java.util.concurrent.TimeUnit;

// 存储字符串
RBucket<String> bucket = redissonClient.getBucket("user:name:1001");

bucket.set("dreamstart", 30, TimeUnit.MINUTES);

String name = bucket.get();

// 删除
bucket.delete();

// 判断是否存在
boolean exists = bucket.isExists();

如果需要缓存 Java 对象:

1
2
3
4
5
6
RBucket<User> bucket =
redissonClient.getBucket("user:1001");

bucket.set(user, 10, TimeUnit.MINUTES);

User cachedUser = bucket.get();

需要确保使用的 Codec 能够正确序列化和反序列化 User。

常用方法速查:

方法 作用
get() 获取对象
set(value) 设置值
set(value, time, unit) 设置值并设置过期时间
delete() 删除键
expire(time, unit) 设置过期时间
setIfAbsent(value) 仅当键不存在时设置
getAndSet(value) 设置新值并返回旧值
compareAndSet(expect, update) 满足预期值时原子更新

其中 setIfAbsent 在部分较老的 Redisson 版本中需要换成 trySet。这些条件更新操作由 Redis 原子执行,不需要为了单键的简单写入再额外加分布式锁。

例如,用于防止同一个请求短时间内重复提交:

1
2
3
4
5
6
7
8
9
10
11
12
String key = "submit:token:" + requestId;

RBucket<String> bucket = redissonClient.getBucket(key);

boolean firstSubmit =
bucket.setIfAbsent("1", 5, TimeUnit.MINUTES);

if (!firstSubmit) {
throw new IllegalStateException("请勿重复提交");
}

// 继续处理业务

注意:这只是 Redis 层面的幂等标记示例。如果涉及真实订单、金额或重要状态变更,仍然需要考虑数据库唯一约束、事务和失败重试等问题。

3.2 RMap:操作类似 Hash 的数据

RMap<K,V> 是分布式 Map,适用于共享属性、按字段读写的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import org.redisson.api.RMap;

RMap<String, String> map =
redissonClient.getMap("user:profile:1001");

// 新增、修改
map.put("name", "Alice");
map.put("city", "Qingdao");

// 查询
String name = map.get("name");

// 不存在时插入
map.putIfAbsent("age", "22");

// 删除
map.remove("city");

// 判断字段
boolean exists = map.containsKey("name");

// 获取整个 Map
java.util.Map<String, String> data = map.readAllMap();

如果不需要获取更新前的旧值,可以使用 fastPut()、fastRemove() 这类方法,减少不必要的返回数据。

注意:RMap 本身不是普通 Java 内存中的 HashMap,每次操作都可能涉及网络通信。

3.3 RMapCache:每个字段单独设置过期时间

RMapCache 特别适合这种场景:

用户维度的数据保存在同一张 Map 中,但每条数据的过期时间各不相同。

1
2
3
4
5
6
7
8
9
10
11
12
import org.redisson.api.RMapCache;

RMapCache<String, String> map =
redissonClient.getMapCache("user:session:map");

// 10 分钟后过期
map.put("1001", "session-A", 10, TimeUnit.MINUTES);

// 30 分钟后过期
map.put("1002", "session-B", 30, TimeUnit.MINUTES);

String session = map.get("1001");

普通 RMap 不提供相同形式的每条记录独立 TTL 操作;需要这种语义时,可以使用 RMapCache。

适用场景包括临时会话、短期状态和有独立过期规则的数据。具体的过期清理实现和功能限制应结合使用的 Redisson 版本查看官方文档。

3.4 RSet、RList、RQueue

这几个接口与 Java 集合的使用思路类似,但数据共享在 Redis 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.redisson.api.RSet;
import org.redisson.api.RList;
import org.redisson.api.RQueue;

// Set:去重
RSet<Long> ids = redissonClient.getSet("user:ids");
ids.add(1001L);
ids.add(1001L);
boolean exists = ids.contains(1001L); // true

// List:有序列表
RList<String> list = redissonClient.getList("notice:list");
list.add("消息1");
list.add("消息2");
String first = list.get(0);

// Queue:先进先出
RQueue<String> queue = redissonClient.getQueue("task:queue");
queue.offer("task-1");
String task = queue.poll();

集合内部的具体实现和复杂度仍由对应的 Redis 结构决定,不要把分布式集合误认为一个没有网络成本的本地集合。

3.5 RScoredSortedSet:排行榜

例如,根据用户积分创建排行榜:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.redisson.api.RScoredSortedSet;

RScoredSortedSet<String> rank =
redissonClient.getScoredSortedSet("rank:score");

// 添加用户及分数
rank.add(100.0, "user-1001");
rank.add(250.0, "user-1002");
rank.add(180.0, "user-1003");

// 增加分数
rank.addScore("user-1001", 20);

// 获取排名靠前的 10 个用户,分数从高到低
java.util.Collection<String> top10 =
rank.valueRangeReversed(0, 9);

// 获取分数
Double score = rank.getScore("user-1002");

// 移除用户
rank.remove("user-1003");

需要注意排名索引通常从 0 开始;如果集合不足 10 个元素,返回结果也会少于 10 个。官方集合 API 还包含 Set、SortedSet、Map、List 等分布式数据结构。

3.6 RAtomicLong:分布式原子计数器

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.redisson.api.RAtomicLong;

RAtomicLong counter =
redissonClient.getAtomicLong("counter:article:1001");

long current = counter.incrementAndGet();

long total = counter.addAndGet(10);

long previous = counter.getAndDecrement();

// 初始化时仅在不存在时设置
counter.compareAndSet(0, 100);

计数器存储在 Redis 中,可以被多个 JVM 共享。像 incrementAndGet() 这样的原子操作适合计数、简单序号等场景。

但是,多个不同的 Redis 操作组合起来,并不自动构成一个整体原子事务。例如先检查库存,再扣减库存,不能仅仅因为使用了 RAtomicLong 就认为整个流程没有竞态。

四、分布式锁:Redisson 最重要的功能

在单体应用中,synchronized 可以协调同一个 JVM 内的线程,但不能直接协调部署在不同机器上的 JVM。

Redisson 的 RLock 提供基于 Redis 的分布式可重入锁,并支持通过发布订阅唤醒等待者。

4.1 最常见的 RLock 用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import org.redisson.api.RLock;

import java.util.concurrent.TimeUnit;

public void createOrder(Long userId) {
RLock lock = redissonClient.getLock("lock:order:" + userId);

boolean locked = false;

try {
// 最多等待 2 秒,具体租约由看门狗续期
locked = lock.tryLock(2, TimeUnit.SECONDS);

if (!locked) {
throw new IllegalStateException("系统繁忙,请重试");
}

// 临界区:执行业务操作
doCreateOrder(userId);

} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException("获取锁时线程被中断", e);

} finally {
// 确保只有持有锁的当前线程才能释放
if (locked && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}

private void doCreateOrder(Long userId) {
// 订单业务
}

这里使用:

1
lock.tryLock(2, TimeUnit.SECONDS);

它表示最多等待 2 秒获取锁,成功后由 Redisson 的看门狗机制维护租约。

如果需要锁定固定时长,也可以使用另一个重载:

1
2
3
boolean locked = lock.tryLock(
2, 30, TimeUnit.SECONDS
);

它表示最多等待 2 秒,获得锁后最多持有 30 秒,到期自动释放。固定租约并不保证业务执行期间锁一定仍然有效:如果业务运行超过 30 秒,锁可能提前释放,因此第二种写法不能机械地套用到耗时不确定的业务中。

4.2 看门狗机制是什么?

Redisson 默认的锁看门狗超时值为 30 秒。当客户端持有锁并且仍然正常运行时,看门狗会续期,避免锁仅因初始 TTL 到期而被释放。具体超时参数可以由 lockWatchdogTimeout 配置。

理解这几个概念:

概念 含义
等待时间 调用方愿意等待多久来获取锁
租约时间 锁最多允许持有多久
看门狗 对未指定固定租约的锁自动续期
unlock() 释放当前线程持有的锁

锁必须在 finally 中按持有情况释放。RLock 的所有权与线程关联,其他线程不能直接替持有者解锁。

4.3 不同锁的选择

以下代码均假设已有 redissonClient。

普通可重入锁

1
RLock lock = redissonClient.getLock("lock:resource");

适合大部分临界区,需要防止多个实例同时修改同一个业务资源时优先考虑。

公平锁

1
RLock lock = redissonClient.getFairLock("lock:resource");

适合希望等待者大体按照排队顺序获取锁的业务。公平性通常伴随额外调度成本,不建议无差别使用。

读写锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.redisson.api.RReadWriteLock;

RReadWriteLock rwLock =
redissonClient.getReadWriteLock("lock:config");

RLock readLock = rwLock.readLock();
RLock writeLock = rwLock.writeLock();

readLock.lock();
try {
// 读操作
} finally {
readLock.unlock();
}

需要修改数据时,使用写锁:

1
2
3
4
5
6
writeLock.lock();
try {
// 修改共享配置
} finally {
writeLock.unlock();
}

多个读锁可以同时持有,而写锁具有独占性。

MultiLock 与 RedLock

Redisson 还提供组合锁和 RedLock 等方案,用于多个锁实例的协调。但它们有不同的拓扑和可靠性假设,不能简单理解为“锁的数量越多就一定越安全”。如果锁保护的是外部存储中的关键写入,还应考虑 fencing token,让资源自身拒绝过期持有者的写操作。

4.4 分布式锁的几个高频错误

错误一:锁名称没有绑定业务资源。

1
redissonClient.getLock("lock:order");

如果所有订单共用这把锁,就会使无关订单相互阻塞。更合理的命名是:

1
redissonClient.getLock("lock:order:" + orderId);

错误二:直接使用固定租约,却不考虑耗时。

如果业务运行超过固定租约,其他实例可能再次拿到锁。业务必须考虑租约、执行时间和并发安全问题。

错误三:在不持有锁的情况下解锁。

这可能触发 IllegalMonitorStateException。始终在 finally 中先判断当前线程是否持有锁。

错误四:认为加锁能代替数据库事务。

分布式锁不是数据库事务。数据库仍然需要正确的事务边界、唯一约束及必要的条件更新。

五、分布式限流:RRateLimiter

例如,需要对所有服务实例共享的登录接口进行限流,每秒最多允许 100 次请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateType;
import org.redisson.api.RateIntervalUnit;

RRateLimiter limiter =
redissonClient.getRateLimiter("limit:login");

// 首次初始化:整个部署共享每秒 100 个许可
limiter.trySetRate(
RateType.OVERALL,
100,
1,
RateIntervalUnit.SECONDS
);

// 每次请求尝试申请一个许可
if (!limiter.tryAcquire()) {
throw new IllegalStateException("请求过于频繁");
}

// 获得许可,继续处理请求

Redisson 的限流器将状态保存在 Redis 中,因此可跨多个客户端共享限制;OVERALL 是整体共享限额,PER_CLIENT 则按客户端分别计量。

关键 API:

方法 语义
trySetRate(...) 仅在尚未配置时初始化
setRate(...) 覆盖限流配置
tryAcquire() 立即尝试获取许可
tryAcquire(n) 一次申请 n 个许可
acquire() 等待许可可用后再继续
availablePermits() 查询当前可用许可数量

生产环境中需要注意:

第一,初始化时使用 trySetRate() 通常更合适。若每次请求都用 setRate() 重设规则,可能覆盖既有配置并影响已经消耗的许可。

第二,按用户限流时,应该使用用户维度的限流器名称,例如:

1
2
String key = "limit:user:" + userId;
RRateLimiter limiter = redissonClient.getRateLimiter(key);

否则所有用户会共用一个限额。

第三,Web 请求通常适合用 tryAcquire() 快速拒绝超限请求,然后通过 HTTP 429 返回限流结果。acquire() 可能阻塞调用线程,因此不要不加判断地用在高并发请求链路中。

六、队列:异步任务与延迟处理

6.1 RBlockingQueue:阻塞队列

1
2
3
4
5
6
7
8
9
10
import org.redisson.api.RBlockingQueue;

RBlockingQueue<String> queue =
redissonClient.getBlockingQueue("queue:jobs");

// 生产者
queue.put("job-1001");

// 消费者:无任务时阻塞等待
String job = queue.take();

put()、take() 适合生产者消费者模式;offer()、poll() 适合不希望一直等待的场景。

在 Spring Boot 项目中,实际使用时应将生产者、消费者分开设计,避免在 HTTP 请求线程里直接无限阻塞消费。

6.2 延迟队列

需要让某个任务等待一段时间再进入可消费队列时,可以使用 RDelayedQueue:

1
2
3
4
5
6
7
8
9
10
11
12
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;

RBlockingQueue<String> readyQueue =
redissonClient.getBlockingQueue("queue:ready");

RDelayedQueue<String> delayedQueue =
redissonClient.getDelayedQueue(readyQueue);

// 30 秒后进入 readyQueue
delayedQueue.offer("send-notification-1001",
30, TimeUnit.SECONDS);

然后由消费者从 readyQueue 获取到期任务。

典型场景包括延迟通知、超时检查和定时触发。但是,延迟队列本身并不意味着业务只执行一次。如果任务处理失败、消费者重启或消息被重复处理,仍然需要幂等性设计。

对于需要严格投递保证、复杂重试、死信队列和丰富监控的企业级消息系统,应结合业务可靠性要求评估专门的消息中间件。

七、发布订阅:RTopic

适合跨实例广播简单通知,例如配置刷新提醒、事件通知。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.redisson.api.RTopic;

// 订阅方
RTopic topic = redissonClient.getTopic("topic:config");

int listenerId = topic.addListener(
String.class,
(channel, message) -> {
System.out.println("收到消息:" + message);
// 更新本地配置缓存
}
);

// 发布方
topic.publish("refresh-config");

// 不再需要时移除监听器
topic.removeListener(listenerId);

Redisson 为 Redis 提供了 Topic 和其他消息相关 API。

这里最容易混淆的是:普通 Redis Pub/Sub 不是可靠消息队列。 离线的订阅者不会自动收到它离线期间错过的历史消息。因此它适合广播通知,不适合独自承担不能丢失的业务任务。

八、信号量与分布式任务同步

8.1 RSemaphore:限制并发数

例如,一个外部服务最多允许 10 个并发任务访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.redisson.api.RSemaphore;

RSemaphore semaphore =
redissonClient.getSemaphore("semaphore:external-api");

// 应由明确的初始化流程设置许可数量
semaphore.trySetPermits(10);

boolean acquired = semaphore.tryAcquire();

if (!acquired) {
// 当前没有可用许可
return;
}

try {
callExternalApi();
} finally {
semaphore.release();
}

RSemaphore 适合限制整体并发任务数,而不只是请求频率。Redisson 也提供带租期的许可信号量等同步器。

注意:共享许可的初始化应当只由受控流程完成;不能每次请求到来时都重新初始化许可数,否则就会破坏并发限制。

另外,与 RLock 不同,普通信号量的许可通常可以由另一个线程释放。但这也意味着必须确保每一次成功申请最终只释放一次,否则会多出不存在的并发容量。

8.2 RCountDownLatch:等待多个任务完成

1
2
3
4
5
6
7
import org.redisson.api.RCountDownLatch;

RCountDownLatch latch =
redissonClient.getCountDownLatch("latch:init");

// 初始化阶段:只在合适时机设置初始计数
latch.trySetCount(3);

三个工作任务完成后,分别执行:

1
latch.countDown();

需要等待任务全部完成的线程执行:

1
2
3
latch.await();

// 到这里说明计数已经归零

这适合等待多个初始化任务、并行任务汇合等场景。

需要注意分布式环境中的重复执行、任务失败和计数未归零等问题。Latch 是协调原语,不会自动替你恢复业务状态。

九、Spring Cache 注解整合

如果你的项目已经习惯使用 @Cacheable、@CachePut 和 @CacheEvict,可以将缓存抽象与 Redisson 结合,而不需要在所有 Service 中直接编写 Redisson 操作。

首先启用 Spring Cache:

1
2
3
4
@EnableCaching
@Configuration
public class CacheConfig {
}

前提是 Spring 容器已经配置了一个以 Redisson 为后端的 CacheManager,具体适配类和依赖需与所用 Redisson 版本匹配;可参考官方 Spring 集成文档。

然后业务代码可以这么写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Service
public class UserService {

@Cacheable(
cacheNames = "user",
key = "#id"
)
public User getUser(Long id) {
// 缓存未命中时执行数据库查询
return queryUserFromDatabase(id);
}

@CachePut(
cacheNames = "user",
key = "#user.id"
)
public User updateUser(User user) {
updateDatabase(user);
return user;
}

@CacheEvict(
cacheNames = "user",
key = "#id"
)
public void deleteUser(Long id) {
deleteFromDatabase(id);
}
}

几个注解的含义:

  • @Cacheable:缓存命中时直接返回缓存结果。

  • @CachePut:执行方法并将返回结果写入缓存。

  • @CacheEvict:移除缓存。

这里有个非常重要的前提:缓存注解通常通过 Spring AOP 代理生效。对象内部通过 this.getUser() 调用同一个类的方法时,可能绕过代理,导致缓存注解不生效。

另外,@CachePut 不应被误认为自动提供数据库与 Redis 的分布式事务。缓存更新失败、并发写入和缓存一致性依然需要独立考虑。

十、布隆过滤器:RBloomFilter

当需要判断某个元素是否可能出现过,并且可以接受一定误判时,可以使用布隆过滤器。

典型用途:在查询数据库前,快速判断一个 ID 是否可能存在,从而减少部分无效查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.redisson.api.RBloomFilter;

RBloomFilter<Long> bloom =
redissonClient.getBloomFilter("bloom:user:ids");

// 预计元素数量 100 万,期望误判率 1%
bloom.tryInit(1_000_000L, 0.01);

// 写入元素
bloom.add(1001L);
bloom.add(1002L);

// 查询
boolean mightContain = bloom.contains(1001L);

需要准确理解布隆过滤器的性质:

  • false 通常意味着元素不在集合中。

  • true 表示元素可能存在,不代表一定存在。

  • 不能把它作为数据库唯一性约束。

  • 要根据容量和期望误判率进行初始化规划。

Redisson 的概率型数据结构还包括 HyperLogLog 等。

GitHub

+1

十一、异步 API 与批量操作

11.1 异步操作

Redisson 提供了异步 API,适合不希望同步等待 Redis 响应的调用链。

1
2
3
4
5
6
7
8
9
10
11
12
RBucket<String> bucket =
redissonClient.getBucket("async:example");

bucket.setAsync("hello")
.whenComplete((result, error) -> {
if (error != null) {
// 记录异常或处理失败
return;
}

// 处理成功结果
});

不同 API 对应的返回类型有所区别,例如 RFuture<T>。在使用异步锁时还需要额外注意线程所有权:一个逻辑操作可能跨越多个线程,所以获取锁与释放锁时必须正确处理线程 ID。

11.2 RBatch:批量执行

当需要对 Redis 执行多条独立操作时,可以考虑批处理,减少逐条请求的网络往返。

1
2
3
4
5
6
7
8
9
10
import org.redisson.api.RBatch;
import org.redisson.api.RFuture;

RBatch batch = redissonClient.createBatch();

batch.getBucket("batch:key:1").setAsync("A");
batch.getBucket("batch:key:2").setAsync("B");
batch.getBucket("batch:key:3").setAsync("C");

batch.execute();

批处理适合批量读写,不过需要注意:

批处理不等于业务事务。 如果多条操作必须作为整体提交,需要另外评估 Redisson 的事务 API、Redis 的执行语义,以及与 MySQL 之间的一致性需求。

十二、生产环境必须注意的问题

  1. 客户端生命周期

让 RedissonClient 由 Spring 管理并在应用退出时关闭。避免在 Service 方法、请求入口或定时任务内部反复 Redisson.create()。

  1. 锁的粒度和释放

锁名应当与具体业务资源绑定,临界区应尽量短;等待超时、固定租约、看门狗续期和异常释放需要一起考虑。

  1. 缓存与数据库一致性

Redis 里的缓存对象不是数据库事实来源的替代品。要设计过期、更新、删除、并发写入和缓存击穿等问题的处理方式。

  1. 连接和阻塞控制

关注 Redis 延迟、连接池利用率、超时、重连和线程池占用。阻塞队列、acquire() 以及同步等待都可能影响业务线程的可用性。

  1. 序列化与版本兼容

跨服务共享对象时,要确保 Codec 与对象结构兼容。谨慎处理类名、字段变更以及不同部署版本之间的数据格式差异。

这些并不是 Redisson 特有的全部问题,而是将 Redis 用在分布式系统里必须面对的工程问题。Redisson 提供了丰富的原语,但不意味着用了 API 就自动获得业务层的强一致性或可靠投递保证。

十三、常用 API 速查表

需求 推荐 API / 方法
保存单个对象 getBucket(key).set(value)
带 TTL 缓存 RBucket.set(value, ttl, unit)
防止重复提交 RBucket.setIfAbsent(...)
按字段存储 getMap(key)
Map 字段独立过期 getMapCache(key)
原子递增 getAtomicLong(key).incrementAndGet()
Set 去重 getSet(key).add(value)
排行榜 getScoredSortedSet(key)
分布式互斥锁 getLock(key)
公平锁 getFairLock(key)
读写锁 getReadWriteLock(key)
整体并发限制 getSemaphore(key)
按频率限流 getRateLimiter(key)
阻塞队列 getBlockingQueue(key)
延迟任务 getDelayedQueue(queue)
广播消息 getTopic(key)
多任务汇合 getCountDownLatch(key)
概率型成员检测 getBloomFilter(key)
批量执行 createBatch()