使用RedisSon实现redis分布式锁
基于你的代码示例和使用场景,以下是Redisson实现Redis分布式锁的核心原理及优化建议的分析:
一、Redisson分布式锁的核心实现原理
1. 可重入锁机制
- Hash结构存储锁信息:Redisson使用Redis的Hash结构存储锁,Key为锁名称(如
xxl:job:时间戳),Field为客户端唯一标识(UUID + 线程ID),Value为重入计数器。- 示例:同一线程多次调用
lock()时,计数器自增;unlock()时计数器自减,归零后删除锁 - 代码优化建议:若业务存在嵌套锁场景(如递归调用),需确保
lock()和unlock()成对出现。
- 示例:同一线程多次调用
2. 锁的原子性操作
- Lua脚本保证原子性:加锁和解锁均通过Lua脚本实现,确保操作(检查锁存在性、更新计数器、设置超时)的原子性。
- 加锁脚本:检查锁是否存在,不存在则创建并设置超时;存在则验证是否为当前线程持有,是则重入。
- 解锁脚本:校验锁归属,计数器减1后若归零则删除锁,并发布解锁消息。
3. 看门狗(WatchDog)机制
- 自动续期防止锁超时:若未显式指定锁超时时间(如
lock.lock()),Redisson默认启动看门狗线程,每10秒(默认)续期锁至30秒,避免业务未完成时锁超时释放- 风险提示:若业务执行时间不可控,需显式指定超时时间(如
lock.lock(10, TimeUnit.SECONDS)),避免锁长期占用
- 风险提示:若业务执行时间不可控,需显式指定超时时间(如
4. 锁竞争与重试机制
- 发布订阅模式:当锁被其他线程持有时,当前线程订阅解锁消息通道,避免轮询消耗资源;收到解锁通知后重新竞争锁
- 可重试逻辑:通过
tryLock()方法支持设置最大等待时间(waitTime),实现阻塞式或非阻塞式重试
- 可重试逻辑:通过
二、代码示例分析及优化建议
1. 当前代码问题分析
RLock lock = XxlJobAdminConfig.getAdminConfig().getRedissonClient().getLock("xxl:job:"+nowTime);
lock.lock(); // 未指定超时时间,依赖看门狗自动续期
try {
preReadSuc = dealTask(nowTime, preReadCount, preReadSuc);
lock.unlock();
} catch (Exception e) {
lock.unlock();
}
- 锁名称设计:锁名称包含时间戳(
nowTime),可能导致每次生成不同锁名,失去互斥意义。建议改为固定业务标识(如xxl:job:task)。 - 未指定锁超时:默认依赖看门狗续期,若任务执行时间过长或进程宕机,可能导致锁无法释放(需结合业务场景权衡)。
- 异常处理冗余:
try-catch中重复调用unlock(),可能引发IllegalMonitorStateException(建议在finally中统一释放)40。
2. 优化后代码示例
String lockKey = "xxl:job:task"; // 固定业务锁名
RLock lock = XxlJobAdminConfig.getAdminConfig().getRedissonClient().getLock(lockKey);
try {
// 显式设置等待时间(10秒)和锁超时(30秒)
boolean acquired = lock.tryLock(10, 30, TimeUnit.SECONDS);
if (acquired) {
preReadSuc = dealTask(nowTime, preReadCount, preReadSuc);
} else {
log.warn("获取锁失败,可能其他节点正在执行任务");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock(); // 仅当前线程持有锁时释放
}
}
三、高级场景与解决方案
以下是针对Redisson分布式锁高级场景的具体代码示例及实现原理分析,结合主从一致性、红锁(RedLock)及公平锁等场景实现:
一、主从一致性与RedLock红锁
场景描述
在Redis主从架构中,主节点宕机时若未完成数据同步,可能导致锁丢失。RedLock通过向多个独立节点(非主从关系)加锁,确保多数节点成功加锁才算获取成功。
代码示例(RedLock实现)
// 创建多个Redisson客户端,连接不同的Redis节点
Config config1 = new Config();
config1.useSingleServer().setAddress("redis://node1:6379");
RedissonClient client1 = Redisson.create(config1);
Config config2 = new Config();
config2.useSingleServer().setAddress("redis://node2:6380");
RedissonClient client2 = Redisson.create(config2);
// 获取多个锁对象
RLock lock1 = client1.getLock("businessLock");
RLock lock2 = client2.getLock("businessLock");
// 创建RedissonRedLock对象
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2);
try {
// 尝试获取RedLock,等待10秒,锁持有30秒
boolean isLocked = redLock.tryLock(10, 30, TimeUnit.SECONDS);
if (isLocked) {
// 业务逻辑(如库存扣减)
deductStock();
}
} finally {
redLock.unlock();
}
关键点:
- 需至少3个独立节点(N/2+1原则),示例简化仅用2个
- 锁名称需一致(
businessLock),RedLock内部通过多数节点加锁成功判定 。
二、公平锁实现
场景描述
公平锁按请求顺序分配锁,避免线程饥饿。Redisson通过Redis的List和ZSet结构实现排队机制。
代码示例(公平锁使用)
// 获取公平锁对象
RLock fairLock = redissonClient.getFairLock("fairTaskLock");
try {
// 尝试获取锁(自动续期)
fairLock.lock();
// 公平执行业务(如订单处理)
processOrder();
} finally {
fairLock.unlock();
}
实现原理:
- 队列机制:通过Redis的List(
redisson_lock_queue:fairTaskLock)记录线程请求顺序 - 超时控制:使用ZSet(
redisson_lock_timeout:fairTaskLock)存储线程预计获取锁的时间,避免死锁 - Lua脚本:检查队列头部线程是否满足获取条件,确保顺序性
三、可重入锁与自动续期
场景描述
同一线程多次获取锁时需支持重入,且业务执行时间不可控时需自动续期。
代码示例(可重入锁)
public void nestedLockExample() {
RLock lock = redissonClient.getLock("reentrantLock");
try {
// 第一次加锁(启动看门狗自动续期)
lock.lock();
// 嵌套调用需要同一把锁的方法
nestedMethod();
} finally {
lock.unlock();
}
}
private void nestedMethod() {
RLock lock = redissonClient.getLock("reentrantLock");
try {
// 重入锁(计数器+1)
lock.lock();
// 业务逻辑(如日志记录)
logOperation();
} finally {
lock.unlock();
}
}
核心机制:
- 重入计数:Redis Hash结构记录线程ID和重入次数(
hincrby操作) - 自动续期:未指定锁超时时间时,看门狗每10秒续期至30秒
- 释放逻辑:
unlock()时计数器递减,归零后删除锁
四、其他高级场景
1. 锁续期手动控制
RLock lock = redissonClient.getLock("manualRenewLock");
try {
lock.lock(10, TimeUnit.SECONDS); // 指定超时时间(禁用看门狗)
// 手动续期(需在超时前调用)
lock.expire(30, TimeUnit.SECONDS);
} finally {
lock.unlock();
}
2. 非公平锁竞争优化
默认使用非公平锁(竞争无序),适合高并发场景:
RLock nonFairLock = redissonClient.getLock("nonFairLock");
nonFairLock.tryLock(0, 30, TimeUnit.SECONDS); // 不等待,直接竞争
总结
- RedLock:通过多节点加锁解决主从一致性问题,需独立节点集群
- 公平锁:依赖Redis队列实现顺序获取,适合资源分配敏感场景
- 可重入锁:利用Hash结构计数,结合看门狗保障长任务执行
开发中需根据业务特点选择锁类型,并结合性能监控优化锁策略(如减少锁粒度、避免嵌套锁竞争)。
四、总结
- 核心优势:Redisson通过Lua脚本、Hash结构、看门狗和发布订阅模型,解决了传统Redis锁的不可重入、不可重试、超时失效等问题
- 注意事项:需合理设计锁名称、显式控制锁超时、处理主从一致性,并在高并发场景结合RedLock增强容错性
- 性能影响:锁竞争频繁时,建议优化业务逻辑(如减少锁粒度)或采用分段锁提升并发度
浙公网安备 33010602011771号