1. 可能存在的问题
- 不可重入:基于SETNX实现的简单分布式锁通常不支持可重入性,即同一个客户端在获取锁后不能再次获取锁,否则会导致死锁。
- 不可重试:如果多个客户端同时尝试获取锁但都失败了,并且没有重试机制,那么所有客户端都会放弃获取锁,导致无法正常运行需要锁的操作。
- 超时释放:客户端处理业务时间过程,引发锁释放的隐患。
- 主从一致性:当一个客户端尝试获取锁时,它将向主节点发送SETNX命令,如果成功设置了锁,那么该客户端获取到了锁。但是,由于Redis是一个主从复制的分布式数据库,主节点和从节点之间存在数据同步的延迟。如果主节点还没有将锁的信息同步到所有的从节点上,这时候如果主节点发生故障或发生了主从切换,那么从节点可能会成为新的主节点,并且新的主节点不会有之前的锁信息。这会导致锁信息在主从切换后丢失,破坏了分布式锁的一致性。
对于主从一致性补充:在主从复制的分布式数据库环境中,主节点上的锁信息会被同步到从节点,但是主从节点之间存在一定的数据同步延迟。因此,可能会出现从节点在延迟期间还不知道新的锁信息的情况。假设一个客户端成功获取了分布式锁,并且这个锁信息已经被写入了主节点的内存中。然而,如果在这个锁信息被完全同步到所有从节点之前发生了主节点故障或主从切换,那么新的主节点可能会不知道这个锁的存在,破坏了分布式锁的一致性。
解决方法:
官网地址: https://redisson.org
GitHub地址:
https://github.com/redisson/redisson
2. Redisson入门案例
注意先登录一个用户之后再启动JMeter
// 一人一单
// 用户id
Long userId = UserHolder.getUser().getId();
// 创建锁对象(注意要拼接用户id,实现单个用户的一人一单,只有同一个id的请求打来时才要进行锁定)
// SimpleRedisLock lock = new SimpleRedisLock(stringRedisTemplate, "order:" + userId);
RLock lock = redissonClient.getLock("order:" + userId);
// redissonClient.getLock()无参默认是获取失败就返回
boolean isLock = lock.tryLock();
if(!isLock){
return Result.fail("一人只能抢一次哦~");
}
try {
// 获取IVoucherOrderService的代理对象
IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
return proxy.createVoucherOrder(voucherId);
} finally {
lock.unlock();
}