使用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();
}

实现原理

  1. 队列机制:通过Redis的List(redisson_lock_queue:fairTaskLock)记录线程请求顺序
  2. 超时控制:使用ZSet(redisson_lock_timeout:fairTaskLock)存储线程预计获取锁的时间,避免死锁
  3. 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增强容错性
  • 性能影响:锁竞争频繁时,建议优化业务逻辑(如减少锁粒度)或采用分段锁提升并发度
posted @ 2025-03-31 23:01  板凳哲学家  阅读(446)  评论(0)    收藏  举报