使用redisson操作redis详解

一、 Redisson简介

Redisson是一个基于Netty框架构建的Java客户端,它架设在Redis之上,提供了一套丰富的分布式对象和服务,它提供了50多个基于Redis的分布式Java对象和服务。与Jedis和Lettuce不同,Redisson将Redis底层数据结构封装成了开发者熟悉的Java对象,让分布式编程变得像本地编程一样直观。

注:Redisson最大的优势在于其看门狗(Watchdog)机制,能够自动为锁续期,彻底解决了分布式锁超时释放的问题。

二、为什么选择 Redisson?

在 Java 生态中,操作 Redis 的客户端库主要有 Jedis、Lettuce 和 Redisson。Jedis 提供了基础的 API,类似于直接发送 Redis 命令;Lettuce 基于 Netty 支持异步和响应式编程;而 Redisson 则走了完全不同的路线。

Redisson 不仅仅是一个 Redis 客户端,它更像是一个驻留在 Redis 服务器上的内存数据网格。它将 Redis 的底层数据结构(如 String、Hash、List、Set、Sorted Set)封装成了 Java 开发人员熟悉的对象(如 Bucket、Map、List、Set、Lock、AtomicLong 等)。这使得开发者可以像操作本地 JVM 集合一样操作分布式数据,极大地降低了分布式编程的复杂性。

与原生客户端相比,Redisson 的核心优势在于:

1.丰富的分布式服务 :提供了分布式锁(可重入锁、公平锁、读写锁)、分布式信号量、分布式闭锁(CountDownLatch)、远程服务、计数器、发布订阅等。

2.自动化的锁续期(看门狗机制) :彻底解决业务执行时间过长导致锁意外释放的问题。

丰富的数据结构映射:将 Redis 的 Hash、List、Set 等直接映射为 Java 的 RMap, RList, RSet,支持异步和响应式编程。

3.高性能与高可用 :支持 Redis 单机、主从、哨兵、集群模式,并内置了连接池管理。

4.高级功能 :内置限流器(RateLimiter)、布隆过滤器(BloomFilter)、分布式任务调度等。

5.自动序列化:默认使用高效的 Jackson JSON 序列化,也可以自定义。

三、如何集成redisson?

1、 引入Maven 依赖

在 pom.xml 中添加 Redisson 核心依赖:

复制代码
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.27.0</version> <!-- 建议使用最新稳定版 -->
</dependency>

2、 配置 Redisson 客户端

复制代码
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonManager {
    private static RedissonClient redissonClient;
 
    private static String redisHost="xxxxx";
    
    private static String redisPort="xxxx";
    
    private static String redisPassword=="xxxxx";
    static {
       Config config = new Config();
        //判断是单个还是集群
        if (StrUtil.isNotEmpty(redisHost)) {
            String[] spitNodes = redisHost.split(",");
            if (spitNodes.length>1) {
                for (int i=0;i<spitNodes.length;i++) {
                    spitNodes[i] = "redis://"+spitNodes[i];
                }
                config.setLockWatchdogTimeout(15000l).useClusterServers()
                        .addNodeAddress(spitNodes).setPassword(redisPassword)
                        .setRetryInterval(2000)
                        .setTimeout(5000)
                        .setConnectTimeout(10000);
            }else {
                String address =  "redis://"+redisHost + ":" + redisPort;
                config.setLockWatchdogTimeout(15000l).useSingleServer()
                        .setAddress(address).setPassword(redisPassword)
                        .setRetryInterval(2000)
                        .setTimeout(5000)
                        .setConnectTimeout(10000);
            }
        }
        redissonClient = Redisson.create(config);
    }
    
    public static RedissonClient getClient() {
          return redissonClient;
    }
}

三、核心功能详解

1、 分布式锁

在分布式系统中,保证共享资源的互斥访问是关键。Redisson 提供了完善的分布式锁实现,支持自动续期(Watchdog)机制,解决了单纯使用 Redis SET NX 无法自动续期的痛点。

1.1、 可重入锁 (RLock)

复制代码
RLock lock = client.getLock("myLock");
try {
    // 尝试加锁,最多等待10秒,锁有效期30秒(之后自动续期)
    // 如果未指定leaseTime,则看门狗机制会默认30秒续期
    boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);
    if (isLocked) {
        try {
            // 业务逻辑处理
            System.out.println("业务处理中...");
            Thread.sleep(30000); // 模拟长业务
            // 注意:这里即使业务超过30秒,由于看门狗机制,锁会被自动续期,直到 unlock 调用
        } finally {
            lock.unlock();
        }
    } else {
        System.out.println("获取锁失败");
    }
} catch (InterruptedException e) {
    e.printStackTrace();
}

看门狗机制:如果未指定 leaseTime,Redisson 会在锁被获取后启动一个后台线程,每隔 (lockTime / 3) 秒自动续期,直到锁被释放。这有效防止了业务执行时间过长导致的锁自动释放问题。

注意:如果手动指定了 leaseTime(如上面的 30 秒),看门狗机制不会生效,锁会在 30 秒后强制释放,无论业务是否执行完。因此,对于不确定执行时间的业务,建议不指定 leaseTime,依靠看门狗自动续期。

1.2、公平锁 (RFairLock):按照请求锁的顺序分配锁,避免某些线程一直抢不到锁。

复制代码
RFairLock fairLock = redissonClient.getFairLock("fairLock");
fairLock.lock();

1.3、读写锁 (RReadWriteLock):提高读多写少场景的性能。

复制代码
RReadWriteLock rwLock = redissonClient.getReadWriteLock("rwLock");
RLock readLock = rwLock.readLock();
RLock writeLock = rwLock.writeLock();
// 读操作
readLock.lock();
try {
    // 读取数据
} finally {
    readLock.unlock();
}

// 写操作
writeLock.lock();
try {
    // 写入数据
} finally {
    writeLock.unlock();
}

1.4、联锁 (RedissonMultiLock):同时锁定多个资源,全部成功才算成功。

复制代码
RLock lock1 = redissonClient.getLock("lock1");
RLock lock2 = redissonClient.getLock("lock2");
RedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2);
multiLock.lock();

1.5、信号量 (RSemaphore):信号量用于控制同时访问资源的线程数量,类似于限流。

复制代码
RSemaphore semaphore = client.getSemaphore("semaphore:limit");
// 设置许可数
semaphore.trySetPermits(10);
try {
    // 获取一个许可,如果没有则阻塞
    semaphore.acquire();
    // 业务逻辑
} finally {
    semaphore.release();
}

1.6、倒计时门栓 (RCountDownLatch):用于等待多个线程完成任务的场景,类似 java.util.concurrent.CountDownLatch

复制代码
RCountDownLatch latch = client.getCountDownLatch("latch:batch");
// 初始化计数(通常在主线程中)
latch.trySetCount(3);
// 工作线程
for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        // 执行任务
        latch.countDown(); // 计数减1
    }).start();
}
// 主线程等待所有工作完成
latch.await(); // 阻塞直到计数为0
System.out.println("all tasks completed.");

2、分布式限流(RateLimiter)

Redisson 提供了基于令牌桶算法的分布式限流器,非常适合接口防刷或控制第三方 API 调用频率。

复制代码
public void apiLimitedAccess() {
    RRateLimiter rateLimiter = redissonClient.getRateLimiter("user:api_limit");
    // 初始化限流规则:每秒允许 10 个请求,桶容量 10个,初始 0 个
    // RateType.OVERALL 表示总限流,RateType.PER_CLIENT 表示按客户端限流
    rateLimiter.trySetRate(RateType.OVERALL, 10, 1, RateIntervalUnit.SECONDS);
    
    // 尝试获取一个许可
    if (rateLimiter.tryAcquire()) {
        System.out.println("请求通过,执行业务");
        // 执行业务
    } else {
        System.out.println("请求被限流,请稍后重试");
        // 返回错误或排队
    }
}

3、 分布式集合与对象

Redisson 将 Redis 数据结构映射为 Java 集合,操作体验与本地集合几乎一致,但所有操作都是网络透明的。

3.1、RBucket (分布式单个对象):用于存储简单的键值对对象

复制代码
RedissonClient client = RedissonManager.getClient();

// 获取一个Bucket(类似 Redis的key)
RBucket<String> bucket = client.getBucket("myBucket");
// 设置值(同步)
bucket.set("姓名1");
// 设置带过期时间的值(10秒后过期)
bucket.set("姓名2", 10, TimeUnit.SECONDS);

// 读取值
String name = bucket.get();
System.out.println(name); // 输出: 姓名2(如果在10秒内)

// 检查是否存在
boolean exists = bucket.isExists();
// 删除
bucket.delete();

3.2、分布式集合 (RList, RSet)

RList(Redis List):

复制代码
RList<String> list= client .getList("task");

list.add("task1");
list.add("task2");
list.add(0, "urgent_task"); // 头部插入

// 阻塞获取(类似LPOP,但非阻塞)
String task = list.remove(0); 
// 获取范围
List<String> subList = list.range(0, 1);

RSet(Redis Set):

复制代码
RSet<String> set = client.getSet("tags");

set.add("java");
set.add("python");
set.add("redis");
boolean isMember = set.contains("java"); // true
// 获取所有元素(慎用于大数据量)
Set<String> all = set.readAll();

3.3、RMap (分布式 HashMap)

复制代码
RMap<String, String> map = client.getMap("user_session");
map.put("token", "asdee233344");
map.put("refreshToken", "def4sd56");
String token = map.get("token");
// 批量操作
Map<String, String> newMap = new HashMap<>();
newMap .put("ip", "192.168.2.1");
newMap .put("device", "PC");
map.putAll(newMap);
// 快速删除
map.remove("refreshToken");
// 使用Fast*方法提高性能(不返回旧值)
map.fastPut("status", "active");
// 遍历
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + "=" + entry.getValue());
}

3.4、 分布式排序集合 (RScoredSortedSet):RScoredSortedSet 对应 Redis 的 Sorted Set(ZSet),常用于排行榜。

复制代码
RScoredSortedSet<Integer> scoreSet = client.getScoredSortedSet("rank");
// 添加成员并指定粉丝量
scoreSet.add(100, "user1");
scoreSet.add(200, "user2");
scoreSet.add(150, "user3");

// 获取排名(降序)
Integer rank = scoreSet.revRank("user2"); // 索引从0开始,分数最高的是最前
// 获取粉丝数
Integer  num= scoreSet.getScore("user1");

// 获取排名前2的玩家(粉丝数降序)
Collection<String> top2 = scoreSet.valueRangeReversed(0, 1);

// 获取粉丝数在 150 到 200 之间的玩家
Collection<String> range = scoreSet.valueRange(150, true, 200, true);

3.5、发布/订阅(PubSub)

复制代码
// 订阅主题
RTopic topic = client.getTopic("news_channel");
int id = topic.addListener(String.class, (channel, msg) -> {
    System.out.println("收到消息: " + msg);
});

// 发布消息
topic.publish("重大消息,springboot3.x发布了");

// 取消订阅
topic.removeListener(id);

四、高级场景实战

1.分布式定时任务调度

利用 RScheduledExecutorService 实现分布式任务调度,任务会在集群中均衡执行,且具备故障转移能力。

复制代码
RScheduledExecutorService executorService = redissonClient.getScheduledExecutorService("myTasks");

// 延迟 10 秒执行
executorService.schedule(() -> {
    System.out.println("任务在执行:" + Thread.currentThread().getName());
    // 业务逻辑
}, 10, TimeUnit.SECONDS);

// 周期性执行:每 10 秒执行一次
executorService.scheduleAtFixedRate(() -> {
    System.out.println("周期性任务");
}, 0, 10, TimeUnit.SECONDS);

2.解决缓存"穿透、击穿、雪崩"

2.1 缓存穿透(Cache Penetration):布隆过滤器拦截

问题:查询根本不存在的数据(如恶意攻击遍历 ID),请求直接穿透缓存打到数据库。

Redisson 方案:使用 RBloomFilter。布隆过滤器判断"不存在"是 100% 准确的,能拦截绝大多数无效请求。

复制代码
// 初始化布隆过滤器(通常在系统启动时执行一次)
public void initBloomFilter() {
    RBloomFilter<String> bloomFilter = client.getBloomFilter("id_filter");
    // 预计插入 100 万个商品 ID,误判率 0.03%
    bloomFilter.tryInit(1000000L, 0.0003);
    // 预加载已知存在的 ID
    for (long i = 1; i <= 1000000; i++) {
        bloomFilter.add("prod_" + i);
    }
}

// 业务查询方法
public Product getProduct(String productId) {
    RBloomFilter<String> bloomFilter = client.getBloomFilter("id_filter");
    
    // 1. 布隆过滤器预判:如果返回 false,则一定不存在,直接返回 null,保护 DB
    if (!bloomFilter.contains(productId)) {
        throw new IllegalArgumentException("商品不存在");
    }
    
    // 2. 查缓存
    RBucket<Product> bucket = client.getBucket("prod_cache:" + productId);
    Product product = bucket.get();
    
    if (product != null) {
        return product;
    }
    
    // 3. 查 DB (此处省略 DB 查询代码)
    product = productService.findById(productId);
    if (product == null) {
        // 即使布隆过滤器通过了,DB 也没有,可能是新数据未同步,可考虑动态添加或缓存空值
        // 注意:布隆过滤器不支持删除,动态添加需谨慎评估误判率影响
        return null; 
    }
    // 4. 回写缓存
    bucket.set(product, 10, TimeUnit.MINUTES);
    return product;
}

2.2、 缓存击穿(Cache Breakdown):互斥锁重建

问题:热点 Key 过期瞬间,大量并发请求同时击穿缓存查 DB,导致 DB 宕机。

Redisson 方案:使用 RLock 实现互斥锁,确保同一时刻只有一个线程去查 DB 重建缓存。

复制代码
public Product getHotProduct(String productId) {
    String cacheKey = "prod_cache:" + productId;
    RBucket<Product> bucket = client.getBucket(cacheKey);
    
    // 1. 查缓存
    Product product = bucket.get();
    if (product != null) {
        return product;
    }
    
    // 2. 缓存失效,尝试获取互斥锁
    RLock lock = redissonClient.getLock("lock:rebuild:" + productId);
    boolean isLocked = false;
    try {
        // 尝试加锁,等待 5 秒,不指定 leaseTime(启用看门狗自动续期)
        // 防止查库时间过长导致锁提前释放
        isLocked = lock.tryLock(5, -1, TimeUnit.SECONDS);
        
        if (isLocked) {
            // 3. 双重检查 (Double-Check):防止锁等待期间其他线程已重建
            product = bucket.get();
            if (product != null) {
                return product;
            }
            
            // 4. 查 DB 重建缓存
            product = productService.findById(productId);
            
            if (product == null) {
                // 防止穿透:缓存空对象,设置短过期时间
                bucket.set(null, 2, TimeUnit.MINUTES);
                return null;
            }
            
            // 5. 写入缓存 (设置随机过期时间防雪崩,见下文)
            bucket.set(product, 15 + new Random().nextInt(5), TimeUnit.MINUTES);
            return product;
        } else {
            // 6. 获取锁失败,说明有其他线程在重建,休眠重试
            Thread.sleep(100);
            return getHotProduct(productId); // 递归重试
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new RuntimeException(e);
    } finally {
        if (isLocked && lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

2.3、缓存雪崩(Cache Avalanche):随机过期时间

问题:大量 Key 在同一时间过期,或 Redis 宕机,导致请求全部涌向 DB。

Redisson 方案:在设置过期时间时增加随机因子,避免集体失效。

复制代码
// 在上面的重建缓存代码中体现:
// 基础过期时间 20 分钟 + 随机 0-5 分钟
long expireTime = 20 + new Random().nextInt(5); 
bucket.set(product, expireTime, TimeUnit.MINUTES);

3、 延迟任务(订单超时取消)

场景:用户下单后 30 分钟未支付,自动取消订单。

复制代码
public void createOrder(Order order) {
    // 1. 保存订单状态为"待支付"
    orderService.save(order);
    
    // 2. 提交延迟任务
    RScheduledExecutorService executor = redissonClient.getScheduledExecutorService("orderTimeoutTasks");
    
    // 任务名称唯一,便于追踪和管理
    String taskId = "order_cancel_" + order.getId();
    
    executor.schedule(new Runnable() {
        @Override
        public void run() {
            // 30 分钟后执行:检查订单状态,若仍为待支付则取消
            Order currentOrder = orderService.findById(order.getId());
            if (currentOrder != null && currentOrder.getStatus() == OrderStatus.UNPAID) {
                orderService.cancel(order.getId());
                System.out.println("订单 " + order.getId() + " 已自动取消");
            }
        }
    }, 30, TimeUnit.MINUTES);
    
    // 可选:将 taskId 存入业务数据,方便手动取消(如用户提前支付了)
    // executor.cancelTask(taskId);
}

4、分布式限流与防刷

利用 RRateLimiter 实现基于令牌桶算法的精准限流,支持单机全局限流或按用户/IP 限流。

接口总限流(保护系统)

限制整个接口的 QPS,防止系统过载。

复制代码
public void handleRequest() {
    RRateLimiter rateLimiter = client.getRateLimiter("apiLimit");
    
    // 初始化:每秒允许 100 个请求,桶容量 100
    // 只需初始化一次,后续调用 trySetRate 不会重置计数,只会更新配置
    rateLimiter.trySetRate(RateType.OVERALL, 100, 1, RateIntervalUnit.SECONDS);
    
    // 尝试获取 1 个许可
    if (rateLimiter.tryAcquire(1)) {
        // 执行业务
        orderService.create();
    } else {
        // 限流降级
        throw new TooManyRequestsException("系统繁忙,请稍后再试");
    }
}

五、 总结

Redisson 极大地降低了分布式系统的开发门槛。它将复杂的分布式协调逻辑封装在简洁的 API 背后,让开发者可以专注于业务本身。

Redisson 通过其独特的对象化设计,将 Redis 从一个简单的键值存储提升到了功能完备的分布式数据结构和协调服务层。无论是简单的数据缓存、复杂的分布式集合操作,还是高难度的分布式锁与并发控制,Redisson 都提供了优雅且高效的解决方案。

1.对于锁:无脑使用 RLock,善用看门狗机制。

2.对于限流:直接使用 RRateLimiter。

3.对于数据结构:用 RMap, RList 替代原生 Redis 命令,代码更优雅。

相关推荐
2301_793804691 小时前
Django全栈开发入门:构建一个博客系统
jvm·数据库·python
weixin_456321642 小时前
生产环境Redis部署选型最佳实践
数据库·redis·缓存
gechunlian882 小时前
数据库(MySQL):使用命令从零开始在Navicat创建一个数据库及其数据表(一).创建基础表
数据库·mysql·oracle
堕2742 小时前
MySQL数据库《基础篇--数据库JDBC编程》
数据库·mysql
FL4m3Y4n2 小时前
Redis相关命令与原理
数据库·redis·缓存
木易 士心2 小时前
自然语言转数据库操作语句原理架构图分析和实现
数据库·后端
TDengine (老段)2 小时前
TDengine IDMP 1-产品简介
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
heimeiyingwang2 小时前
【架构实战】缓存架构 Redis 集群部署
redis·缓存·架构
娇娇yyyyyy2 小时前
QT编程(20): Qt QListWidget QTreeWidget介绍
数据库·qt·microsoft