
Redisson 详解Redisson 是基于 Redis 实现的Java 驻内存数据网格In‑Memory Data Grid封装了 Redis 原生命令提供大量分布式 Java 对象与分布式服务解决原生 Jedis/Lettuce 很难实现的分布式问题分布式锁、分布式集合、分布式限流、分布式事务、延迟队列等。底层通信基于 Netty异步非阻塞支持 Redis 单机、主从、哨兵、集群、Redis‑Proxy 多种部署模式。一、对比Jedis/Lettuce偏底层直接操作 Redis 命令业务自己封装分布式锁、队列等容易踩坑锁过期、锁续期、锁可重入、锁释放误删。Redisson高层 API把 Redis 能力封装成 Java 对象像使用本地List、Map、Lock一样使用分布式组件可以处理大部分异常边界。Maven 依赖dependency groupIdorg.redisson/groupId artifactIdredisson-spring-boot-starter/artifactId version3.33.0/version /dependency二、基础使用1. 配置application.ymlspring: redis: host: 127.0.0.1 port: 6379 password: redisson starter 会复用 spring.redis 配置也可以自定义redisson.conf文件做复杂集群配置。2. 简单获取 Bean 使用Autowired private RedissonClient redissonClient; // 获取分布式锁 RLock lock redissonClient.getLock(my_lock);使用redisson‑spring‑boot‑starter启动器时不需要自己手动写 Bean 注册 RedissonClientstarter 自动装配。原因starter 自动装配逻辑redisson‑spring‑boot‑starter内部自动配置类RedissonAutoConfiguration读取spring.redis.*配置自动构建RedissonClient对象放入 Spring 容器所以直接Autowired private RedissonClient redissonClient;就能注入成功。// 直接注入即可不用自己写配置类 Autowired private RedissonClient redissonClient;1. 逐部分拆解AutowiredSpring 的注解Spring Boot / Spring MVC自动依赖注入。作用Spring IoC 容器里去找一个类型为RedissonClient的 Bean自动赋值给这个成员变量不用自己写new RedissonClient()。前提项目已经配置好 Redisson容器中存在 RedissonClient 实例。 一般两种配置方式引入redisson-spring-boot-starteryml 配置 redis 地址starter 自动帮你创建 RedissonClient Bean手动写Bean方法返回RedissonClient对象注册到 Spring 容器。private RedissonClient redissonClient;RedissonClientRedisson 的核心客户端接口整个 Redisson 功能入口。前面写的redissonClient.getLock(lockKey)就是调用这个对象的方法获取分布式锁。2. 它能干哪些事不止 getLockRedissonClient是入口除了分布式锁 RLock还提供大量 Redis 封装对象RLock getLock(String name); // 可重入锁 RBucketT getBucket(String name); // String类型缓存 RMapK,V getMap(String name); // hash RTopic getTopic(String name); // 发布订阅 RCountDownLatch getCountDownLatch(String name); // 分布式闭锁3. 底层原理Spring 启动时实例化当前这个类的时候发现属性上加了Autowired就去容器查找RedissonClient类型 Bean找到后反射赋值给redissonClient变量。所以在方法里直接使用redissonClient不用手动实例化。三、最核心分布式锁 RLock原生 Redis 做分布式锁常见问题不可重入、锁超时业务没执行完、锁被别的线程释放、主从切换锁丢失。RedissonRLock解决绝大多数问题。RLock lock redissonClient.getLock(order_lock); // 尝试获取锁waitTime最多等待时间leaseTime锁持有时间单位秒 boolean ok lock.tryLock(10,30, TimeUnit.SECONDS); try{ if(ok){ // 执行业务 } }finally { // 判断当前线程持有锁才释放防止释放别人的锁 if(lock.isHeldByCurrentThread()){ lock.unlock(); } }✨看门狗机制自动续期重点当不指定 leaseTime调用lock()/tryLock(waitTime,-1,unit)开启看门狗默认看门狗超时时间lockWatchdogTimeout 30s获取锁成功后后台启动异步定时任务每 10s30/3给锁续期到 30 秒只要线程不释放锁业务还在运行锁就不会过期。线程释放锁看门狗停止如果 JVM 宕机锁 30s 后自动释放。⚠️ 如果手动指定 leaseTime看门狗失效完全靠你设置的过期时间。业务执行超过 leaseTime 锁会直接释放需要业务自己处理。RLock 几个锁类型1. RLock 可重入锁同一个线程可以多次 lock计数多次 unlock 才真正释放。底层 lua 脚本保证原子。2. RWLock RReadWriteLock 读写锁读锁共享写锁互斥大量读少写场景。核心特性读共享、写互斥读锁ReadLock多个线程 / 客户端可以同时加读锁并发读互不阻塞。写锁WriteLock独占锁一旦写锁持有所有其他读锁、写锁全部阻塞等待。锁互斥规则当前持有读请求写请求读锁✅放行❌阻塞写锁❌阻塞❌阻塞无锁✅放行✅放行锁可重入同一个线程读锁、写锁都支持重入写锁可以降级到读锁写线程可以加读锁读锁不能升级写锁读线程不能直接获取写锁会永久阻塞这是非常容易踩坑的点。支持锁超时、等待时间、自动续期看门狗和 Redisson 普通锁机制一致。⚠️重要坑读锁不能升级写锁线程拿到读锁之后再尝试获取写锁 → 死锁阻塞。 正确先释放读锁再加写锁或者直接上写锁。 写锁可以降级拿到写锁后再加读锁之后释放写锁此时持有读锁。// 获取读写锁对象 RReadWriteLock rwLock redissonClient.getReadWriteLock(rw); // 获取读锁 RLock readLock rwLock.readLock(); // 获取写锁 RLock writeLock rwLock.writeLock();readLock()、writeLock()返回的都是RLock和普通分布式锁 API 完全一致lock()阻塞获取锁看门狗自动续期lock(long waitTime, long leaseTime, TimeUnit unit)指定等待时间、锁过期时间tryLock()非阻塞尝试获取unlock()释放锁isHeldByCurrentThread()判断当前线程是否持有锁完整代码示例import org.redisson.api.RLock; import org.redisson.api.RReadWriteLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; Service public class RwLockBizService { // 直接注入不需要自己new RedissonClient不需要手写Config Autowired private RedissonClient redissonClient; /** * 读操作加【读锁】读共享多线程可以同时获取读锁 */ public void readOperation() { // 获取分布式读写锁redis key rw:demo RReadWriteLock rwLock redissonClient.getReadWriteLock(rw:demo); // 获取读锁 RLock readLock rwLock.readLock(); try { // 阻塞获取读锁看门狗自动续期 readLock.lock(); System.out.println(Thread.currentThread().getName() 获取读锁开始读取共享数据); // 模拟业务读取耗时 Thread.sleep(2000); System.out.println(Thread.currentThread().getName() 读取数据完成); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // ⚠️释放锁前必须判断当前线程是否持有锁防止释放别的线程锁 if (readLock.isHeldByCurrentThread()) { readLock.unlock(); System.out.println(Thread.currentThread().getName() 释放读锁); } } } /** * 写操作加【写锁】独占锁必须等待所有读锁全部释放才能拿到写锁 */ public void writeOperation() { RReadWriteLock rwLock redissonClient.getReadWriteLock(rw:demo); RLock writeLock rwLock.writeLock(); try { writeLock.lock(); System.out.println(Thread.currentThread().getName() 获取写锁开始修改共享数据); // 模拟写业务耗时 Thread.sleep(2000); System.out.println(Thread.currentThread().getName() 修改数据完成); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { if (writeLock.isHeldByCurrentThread()) { writeLock.unlock(); System.out.println(Thread.currentThread().getName() 释放写锁); } } } }测试类import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; RestController public class LockTestController { Autowired private RwLockBizService rwLockBizService; // 测试读锁可以并发调用 GetMapping(/read) public String read() { new Thread(() - rwLockBizService.readOperation(), 读线程‑A).start(); new Thread(() - rwLockBizService.readOperation(), 读线程‑B).start(); return 已发起读请求; } // 测试写锁 GetMapping(/write) public String write() { new Thread(() - rwLockBizService.writeOperation(), 写线程‑X).start(); return 已发起写请求; } }生产常用tryLock 带等待时间版本不无限阻塞业务不要无脑用lock()生产优先使用带等待时间避免无限阻塞public void writeOperationWithTryLock() { RReadWriteLock rwLock redissonClient.getReadWriteLock(rw:demo); RLock writeLock rwLock.writeLock(); boolean lockOk false; try { // 最多等待3秒获取锁拿到锁后最多10秒自动过期释放 lockOk writeLock.tryLock(3, 10, java.util.concurrent.TimeUnit.SECONDS); if (lockOk) { System.out.println(Thread.currentThread().getName() 获取写锁成功执行业务更新); Thread.sleep(2000); } else { System.out.println(获取写锁超时失败做降级/返回提示); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // lockOk为true才去释放锁并且校验是否当前线程持有 if (lockOk writeLock.isHeldByCurrentThread()) { writeLock.unlock(); } } }⚠️注意如果你指定了leaseTime第二个参数 10 秒看门狗自动续期失效业务必须保证在 leaseTime 内完成业务时长不确定就用不带 leaseTime 的lock()启用看门狗。重点坑点❌读锁不能升级写锁拿到读锁之后直接申请写锁 → 永久阻塞死锁。// 错误示范 RLock readLock rwLock.readLock(); readLock.lock(); try{ // 这里再调用 writeLock.lock() → 死锁禁止 }finally { readLock.unlock(); }✅正确先释放读锁再加写锁。✅写锁可以降级读锁拿到写锁可以获取读锁之后释放写锁线程保留读锁。finally里面释放锁一定要搭配isHeldByCurrentThread()。 如果锁获取失败不要去 unlock会抛异常。生产常用tryLock 带等待时间版本不无限阻塞业务不要无脑用lock()生产优先使用带等待时间避免无限阻塞public void writeOperationWithTryLock() { RReadWriteLock rwLock redissonClient.getReadWriteLock(rw:demo); RLock writeLock rwLock.writeLock(); boolean lockOk false; try { // 最多等待3秒获取锁拿到锁后最多10秒自动过期释放 lockOk writeLock.tryLock(3, 10, java.util.concurrent.TimeUnit.SECONDS); if (lockOk) { System.out.println(Thread.currentThread().getName() 获取写锁成功执行业务更新); Thread.sleep(2000); } else { System.out.println(获取写锁超时失败做降级/返回提示); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // lockOk为true才去释放锁并且校验是否当前线程持有 if (lockOk writeLock.isHeldByCurrentThread()) { writeLock.unlock(); } } }⚠️注意如果你指定了leaseTime第二个参数 10 秒看门狗自动续期失效业务必须保证在 leaseTime 内完成业务时长不确定就用不带 leaseTime 的lock()启用看门狗。重点注意❌读锁不能升级写锁拿到读锁之后直接申请写锁 → 永久阻塞死锁。// 错误示范 RLock readLock rwLock.readLock(); readLock.lock(); try{ // 这里再调用 writeLock.lock() → 死锁禁止 }finally { readLock.unlock(); }✅正确先释放读锁再加写锁。✅写锁可以降级读锁拿到写锁可以获取读锁之后释放写锁线程保留读锁。finally里面释放锁一定要搭配isHeldByCurrentThread()。 如果锁获取失败不要去 unlock会抛异常。3. RSemaphore 信号量分布式限流控制同时 N 个许可。4. RPermitExpirableSemaphore 可过期信号量5. RLock fairLock 公平锁按申请顺序获取锁Redis zset 实现性能略差。锁底层全部使用Lua 脚本保证多步操作原子性避免并发漏洞。Redisson 各类分布式锁完整梳理Redisson 提供非常丰富的分布式锁全部基于 Redis 实现适配不同并发场景锁类型获取方式核心特点适用场景可重入锁RLockredissonClient.getLock(key)最常用可重入、看门狗续期、独占大部分独占业务场景更新、扣减、防并发读写锁 RReadWriteLockredissonClient.getReadWriteLock(key)读共享写互斥读不能升级写写可降级读读多写少缓存、配置数据公平锁 FairLockredissonClient.getFairLock(key)按请求先后顺序获取锁FIFO 排队对锁获取顺序有要求避免饥饿联锁 MultiLockredissonClient.getMultiLock(lock1,lock2,lock3)同时锁住多个 key全部成功才算拿到锁需要同时锁定多个资源多把锁原子获取红锁 RedLockredissonClient.getRedLock(lock1,lock2,lock3)多个独立 Redis 节点过半节点拿到锁才算成功Redis 无主从追求高可用生产谨慎信号量 RSemaphoreredissonClient.getSemaphore(key)分布式版 Semaphore控制许可数量限流、资源池限流、控制并发数量最多 N 个线程同时执行可过期信号量redissonClient.getPermitExpirableSemaphore(key)信号量 每个许可自带过期时间租赁资源许可会自动过期回收闭锁 RCountDownLatchredissonClient.getCountDownLatch(key)分布式 CountDownLatch等待 N 个任务完成多实例任务协同等待一批异步任务完成❗锁的主从问题RLock 普通锁主节点拿到锁就返回成功主还没同步到从就宕机新主没有锁出现锁丢失。 解决RedissonRedLock(红锁)向 N 个独立 Redis 主节点申请锁过半成功才算拿到锁代价高生产慎用。四、常用分布式对象1. 集合类分布式跨 JVM 共享RMapString,String map redissonClient.getMap(dist_map); RListString list redissonClient.getList(dist_list); RSetString set redissonClient.getSet(dist_set); RQueueString queue redissonClient.getQueue(dist_queue);特点API 和 JDK 集合几乎一致支持部分本地缓存模式RMapLocalCache提升读取性能。2. 分布式布隆过滤器 RBloomFilterRBloomFilterString bloom redissonClient.getBloomFilter(bloom); bloom.tryInit(1000000,0.01); //预期数量误判率 bloom.add(user1); bloom.contains(user1);3. 原子类 RAtomicLongRAtomicLong atomic redissonClient.getAtomicLong(cnt); atomic.incrementAndGet();五、强大的队列1. RBlockingQueue 阻塞队列生产者消费者模式take () 阻塞等待元素。RBlockingQueueString bq redissonClient.getBlockingQueue(bq); //消费者 String item bq.take(); //阻塞有数据才返回2. RDelayedQueue 延迟队列非常常用基于 zset实现任务延迟执行比如订单 30 分钟未支付关闭。RBlockingQueueString queue redissonClient.getBlockingQueue(order_queue); RDelayedQueueString delayedQueue redissonClient.getDelayedQueue(queue); // 投递10秒后到达目标queue delayedQueue.offer(order_1001,10,TimeUnit.SECONDS); // 消费从原始queue take String task queue.take();注意服务重启任务不会丢任务数据放在 Redis缺点不能精准删除任务业务需要自己做状态过滤。六、Redisson 事务与 RCacheRTransaction 分布式事务Redis Multi 事务不是分布式 XA只是 Redis 单机事务不支持跨 Redis 节点事务。RCache实现 Spring Cache作为 Spring 缓存底层支持 TTL、缓存淘汰。七、Redis 多部署模式支持单机单节点 Redis主从 Replication写主读从Sentinel 哨兵自动主从故障转移Redis Cluster 集群分片slot 哈希槽Redisson 自动处理槽迁移、重定向。Proxy 模式Redis‑Proxy、codis。八、常见踩坑点leaseTime 指定后看门狗失效业务执行时间不能超过 leaseTime不确定业务耗时就不要填 leaseTime交给看门狗。unlock 必须放在 finally必须判断 isHeldByCurrentThread否则会抛异常或者释放别的线程锁。红锁 RedissonRedLock 生产谨慎使用运维成本高大部分业务接受极小概率锁失效。延迟队列不能精准删除任务业务要做幂等拿到任务先判断业务状态。序列化默认 Jackson 序列化也可以修改为 protobuf/kryo。Redis Cluster 槽迁移Redisson 内部自动处理 MOVED 重定向。锁可重入同一个线程多次 lock 计数 1必须对应次数 unlock。九、Redisson vs 手写 Redis 锁方案优点缺点手写 Redis 锁setnxlua简单无额外依赖需要自己实现可重入、看门狗续期、锁释放判断、异常处理极易漏边界Redisson RLock开箱即用看门狗、可重入、lua 原子、各种锁模型引入第三方组件理解看门狗机制十、看门狗完整流程小结调用 lock ()不设置 leaseTimelua 脚本设置 hash 存锁key 锁名field 线程 idvalue 重入次数expire30s获取锁成功启动定时任务每 10s 执行 lua给锁重置过期时间 30s业务执行完 unlock删除 key停止定时任务如果服务崩溃定时任务消失锁 30s 过期自动释放。会不会无限续✅业务正常运行、线程存活、没有释放锁 → 会一直循环续期❌进程宕机 / 线程终止 → 立刻停止续期锁超时释放不会永久占有锁。也就是业务跑多久锁就被续多久业务挂了锁 30s 后释放。7. 为什么间隔是 1/3预留足够容错窗口防止网络抖动一次续期请求丢失还有两轮时间兜底不至于锁直接过期。