Redisson分布式锁使用指南

 

一、Redisson分布式锁基础用法

1.1 基础依赖配置

1.2 基本锁操作

二、高级特性使用

2.1 公平锁(Fair Lock)

2.2 读写锁(ReadWrite Lock)

2.3 联锁(MultiLock)

2.4 红锁(RedLock)

三、锁失效场景及解决方案

3.1 锁过期时间设置不当

3.2 未正确处理锁释放

3.3 锁重入问题

3.4 集群环境下的特殊问题

四、最佳实践与注意事项

4.1 锁命名规范

4.2 避免锁粒度过粗或过细

4.3 超时和重试策略

4.4 监控与日志

4.5 性能优化建议

五、常见问题排查清单

六、总结


一、Redisson分布式锁基础用法

1.1 基础依赖配置

<!-- Maven依赖配置 -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.18.0</version>
</dependency>
 

yaml

singleServerConfig:
  address: "redis://127.0.0.1:6379"
  database: 0
  connectionPoolSize: 64
  connectionMinimumIdleSize: 10
 

1.2 基本锁操作

// 获取分布式锁实例
RLock lock = redissonClient.getLock("myLock");

// 方式1:阻塞式加锁(推荐用法)
lock.lock();
try {
    // 执行业务逻辑
    executeBusiness();
} finally {
    // 确保锁被释放
    lock.unlock();
}

// 方式2:带超时的尝试加锁
boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);
if (isLocked) {
    try {
        executeBusiness();
    } finally {
        lock.unlock();
    }
}

// 方式3:异步加锁
RFuture<Void> lockFuture = lock.lockAsync();
lockFuture.whenComplete((result, exception) -> {
    try {
        executeBusiness();
    } finally {
        lock.unlockAsync();
    }
});
 

二、高级特性使用

2.1 公平锁(Fair Lock)

// 获取公平锁实例(按请求顺序获得锁)
RLock fairLock = redissonClient.getFairLock("fairLock");
fairLock.lock();
try {
    // 业务逻辑
} finally {
    fairLock.unlock();
}

2.2 读写锁(ReadWrite Lock)

// 读写锁实现读多写少场景
RReadWriteLock rwLock = redissonClient.getReadWriteLock("rwLock");

// 读锁(共享锁,允许多个线程同时持有)
RLock readLock = rwLock.readLock();
readLock.lock();
try {
    // 读取操作
} finally {
    readLock.unlock();
}

// 写锁(排他锁,只能有一个线程持有)
RLock writeLock = rwLock.writeLock();
writeLock.lock();
try {
    // 写入操作
} finally {
    writeLock.unlock();
}

2.3 联锁(MultiLock)

// 同时锁定多个资源
RLock lock1 = redissonClient.getLock("lock1");
RLock lock2 = redissonClient.getLock("lock2");
RLock lock3 = redissonClient.getLock("lock3");

RedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2, lock3);
multiLock.lock();
try {
    // 操作多个资源的业务逻辑
} finally {
    multiLock.unlock();
}

2.4 红锁(RedLock)

// 红锁(RedLock算法,需要多个Redis实例)
Config config1 = new Config();
config1.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient client1 = Redisson.create(config1);

Config config2 = new Config();
config2.useSingleServer().setAddress("redis://127.0.0.1:6380");
RedissonClient client2 = Redisson.create(config2);

RLock lock1 = client1.getLock("lock");
RLock lock2 = client2.getLock("lock");

RedissonRedLock redLock = new RedissonRedLock(lock1, lock2);
redLock.lock();
try {
    // 高安全要求的业务逻辑
} finally {
    redLock.unlock();
}

三、锁失效场景及解决方案

3.1 锁过期时间设置不当

// ❌ 错误示例:锁过期时间太短
lock.lock(5, TimeUnit.SECONDS);  // 业务如果超过5秒会出问题

// ✅ 正确做法:合理评估并设置锁超时时间
// 或者使用看门狗机制(默认30秒,每10秒续期)
lock.lock();  // 使用看门狗自动续期

// ✅ 或明确指定足够长的超时时间
lock.lock(30, TimeUnit.SECONDS);

3.2 未正确处理锁释放

// ❌ 错误示例:未在finally中释放锁
lock.lock();
executeBusiness();
// 如果这里抛出异常,锁永远不会释放

// ✅ 正确示例:确保锁释放
lock.lock();
try {
    executeBusiness();
} finally {
    // 检查当前线程是否持有锁
    if (lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

3.3 锁重入问题

// ❌ 错误示例:在递归调用中重复加锁
public void recursiveMethod(int n) {
    lock.lock();
    try {
        if (n > 0) {
            recursiveMethod(n - 1);  // 重复获取锁,造成死锁
        }
    } finally {
        lock.unlock();
    }
}

// ✅ 正确示例:Redisson锁支持可重入
public void recursiveMethod(int n) {
    lock.lock();
    try {
        if (n > 0) {
            recursiveMethod(n - 1);  // 可重入锁,不会死锁
        }
    } finally {
        lock.unlock();
    }
}

3.4 集群环境下的特殊问题

// ❌ 错误示例:主从切换导致锁丢失
// 在主从架构中,主节点写入锁后未同步到从节点就宕机

// ✅ 解决方案1:使用红锁(RedLock)
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);

// ✅ 解决方案2:使用高可用集群模式
Config config = new Config();
config.useClusterServers()
    .addNodeAddress("redis://127.0.0.1:7001")
    .addNodeAddress("redis://127.0.0.1:7002")
    .addNodeAddress("redis://127.0.0.1:7003");

四、最佳实践与注意事项

4.1 锁命名规范

// ✅ 使用业务相关的有意义的锁名
// 格式:业务:资源类型:资源标识
String lockKey = "order:pay:lock:" + orderId;
RLock lock = redissonClient.getLock(lockKey);

// ❌ 避免使用通用锁名
RLock badLock = redissonClient.getLock("lock");  // 过于通用,易冲突

4.2 避免锁粒度过粗或过细


// ❌ 锁粒度过粗(性能瓶颈)
RLock globalLock = redissonClient.getLock("globalOrderLock");

// ❌ 锁粒度过细(管理复杂)
RLock itemLock = redissonClient.getLock("orderItem:" + itemId + ":" + userId);

// ✅ 合适的锁粒度
RLock orderLock = redissonClient.getLock("order:" + orderId);

4.3 超时和重试策略

// 配置合理的重试策略
boolean locked = false;
int retryCount = 0;
int maxRetries = 3;

while (!locked && retryCount < maxRetries) {
    try {
        locked = lock.tryLock(100, 10000, TimeUnit.MILLISECONDS);
        if (locked) {
            // 执行业务逻辑
            executeBusiness();
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        break;
    } finally {
        if (locked) {
            lock.unlock();
        }
    }
    retryCount++;
    if (!locked && retryCount < maxRetries) {
        Thread.sleep(100 * retryCount); // 指数退避
    }
}

4.4 监控与日志

// 添加锁操作的监控点
@Slf4j
@Component
public class DistributedLockService {
    
    @Autowired
    private RedissonClient redissonClient;
    
    public <T> T executeWithLock(String lockKey, long waitTime, long leaseTime, 
                                Supplier<T> supplier) {
        RLock lock = redissonClient.getLock(lockKey);
        long start = System.currentTimeMillis();
        
        try {
            boolean acquired = lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
            if (!acquired) {
                log.warn("获取锁失败,lockKey: {}", lockKey);
                throw new LockAcquireException("获取分布式锁失败");
            }
            
            log.info("成功获取锁,lockKey: {}, 耗时: {}ms", 
                    lockKey, System.currentTimeMillis() - start);
            
            return supplier.get();
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new LockAcquireException("锁获取被中断", e);
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("释放锁,lockKey: {}", lockKey);
            }
        }
    }
}

4.5 性能优化建议

# 优化配置示例
lockWatchdogTimeout: 30000  # 看门狗超时时间
keepPubSubOrder: true       # 保持订阅顺序
retryInterval: 300          # 重试间隔(ms)
retryAttempts: 3            # 重试次数
timeout: 3000               # 命令超时时间(ms)

五、常见问题排查清单

问题现象 可能原因 解决方案
锁无故释放 业务执行时间超过锁过期时间 合理设置leaseTime或使用看门狗
锁无法释放 未在finally中释放锁 确保锁释放逻辑在finally块中
锁竞争激烈 锁粒度过粗 细化锁粒度,按资源拆分
Redis连接失败 网络问题或Redis宕机 检查网络,配置连接池和重试机制
死锁 锁重入问题或异常未释放锁 使用可重入锁,确保异常时释放锁

六、总结

Redisson分布式锁提供了强大的功能和灵活的配置选项,但在使用时需要注意:

  1. 合理设置超时时间:避免业务未完成锁已过期

  2. 确保锁必定释放:在finally块中释放锁

  3. 选择合适的锁类型:根据场景选择普通锁、公平锁、读写锁等

  4. 注意锁粒度:平衡并发性能和数据安全

  5. 考虑高可用:在集群环境下使用红锁或集群模式

  6. 添加监控:记录锁的获取和释放情况,便于排查问题

通过遵循以上最佳实践,可以有效避免分布式锁的常见问题,确保分布式系统的数据一致性和高可用性。

Logo

更多推荐