redis 分布式锁的 5个坑一、锁未被释放 问题描述:在高并发情况下,如果线程获取到锁后,由于异常或其他原因没有释放锁
一、锁未被释放
-
问题描述:在高并发情况下,如果线程获取到锁后,由于异常或其他原因没有释放锁,会导致其他线程无法获取到锁,从而影响程序的正常运行。
-
解决方案:确保在
finally
块中释放锁,以保证锁一定会被释放。 -
代码示例:
public String stockLock() {
RLock lock = redissonClient.getLock("stockLock");
try {
if (lock.tryLock(10, TimeUnit.SECONDS)) {
// 业务逻辑处理
}
} finally {
lock.unlock();
}
return "ok";
}
二、B的锁被A给释放了
-
问题描述:多个线程或进程使用相同的锁值去释放锁,可能会导致一个线程释放了另一个线程的锁。
-
解决方案:使用一个唯一标识符(如UUID)作为锁的值,确保只有持有相同标识符的线程才能释放锁。
-
代码示例:
String uniqueId = UUID.randomUUID().toString();
RLock lock = redissonClient.getLock("stockLock");
if (lock.tryLock()) {
try {
// 业务逻辑处理
} finally {
if (lock.isHeldByCurrentThread() && lock.getHoldCount() > 0) {
lock.unlock();
}
}
}
三、数据库事务超时
-
问题描述:在分布式系统中,数据库事务可能会因为执行时间过长而超时,导致锁的释放和业务逻辑执行不一致。
-
解决方案:避免在分布式锁的获取和释放过程中使用数据库事务,或者手动控制事务的提交和回滚。
-
代码示例:
@Autowired
DataSourceTransactionManager transactionManager;
public void processWithLock() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
RLock lock = redissonClient.getLock("stockLock");
if (lock.tryLock()) {
try {
// 执行业务逻辑
} finally {
lock.unlock();
}
}
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
}
}
四、锁过期了,业务还没执行完
-
问题描述:在业务逻辑执行时间较长的情况下,锁可能会在业务逻辑执行完毕前过期,导致其他线程可以获取到锁。
-
解决方案:使用具有自动续期功能的客户端(如Redisson),它会在锁即将过期时自动续期。
-
代码示例:
RLock lock = redissonClient.getLock("stockLock");
if (lock.tryLock()) {
try {
// 执行业务逻辑
// Redisson会自动续期锁
} finally {
lock.unlock();
}
}
五、redis主从复制的坑
-
问题描述:在Redis主从复制模式下,如果主节点宕机,从节点晋升为主节点,可能会导致多个客户端同时持有锁。
-
解决方案:避免在主从复制模式下使用分布式锁,或者使用Redis Cluster模式来提高锁的安全性。
-
代码示例:
// 在Redis Cluster模式下使用分布式锁
RLock lock = redissonClient.getLock("stockLock");
if (lock.tryLock()) {
try {
// 执行业务逻辑
} finally {
lock.unlock();
}
}
转载自:https://juejin.cn/post/7414734289131470867