目录
前文链接:
主要是要学习redis分布锁的原理,其实redis有专门用来处理这些问题的工具
Redisson可以解决以下四个问题
Redisson
之所以引用Redisson是因为前文我们自己实现的锁不满足可重入性(什么是 “可重入”:可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁)
Redisson入门
实现
@Resource
private RedissonClient redissonClient;
@Transactional
public Result createVoucherOrder(Long voucherId) {
//获取用户Id
UserDTO user = UserHolder.getUser();
Long userId = user.getId();
//创建锁对象
RLock redisLock = redissonClient.getLock("lock:order:" + userId);
//尝试获取锁
boolean isLock = redisLock.tryLock();
//判断
if(!isLock){
//获取锁失败,直接返回失败或者重试
return Result.fail("不允许重复下单");
}
try {
//一人一单
Integer count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
if(count>0){
//说明用户之前买过了
return Result.fail("用户已经购买过一次");
}
VoucherOrder voucherOrder = new VoucherOrder();
//扣减库存
seckillVoucherService.update()
.setSql("stock = stock - 1")
.eq("voucher_id",voucherId).gt("stock",0)
.update();
//设置订单Id
long orderId = redisIdWorker.nextId("order");
voucherOrder.setId(orderId);
//设置优惠券Id
voucherOrder.setVoucherId(voucherId);
//设置用户Id
voucherOrder.setUserId(userId);
save(voucherOrder);
//返回订单Id
return Result.ok(orderId);
} finally {
//释放锁
redisLock.unlock();
}
}
Redisson可重入锁原理(解决不可重入)
通过记录获取锁的数目,可以据此来判断当前是否是在最外围;
因为操作需要具有原子性,所以得采用Lua脚本进行编写
Lua实现获取锁
Lua脚本实现释放锁
可以深入Redisson获取锁与释放锁的流程,可以看到Lua脚本、
获取锁:
释放锁:
Redisson的深入理解(解决不可重试和超时释放)
锁重试:不是一直尝试获取锁,而是等待释放锁的信号量
WatchDog机制:简单来说就是每过一段时间去重置一下有效期;(如果负责储存这个分布式锁的Redisson节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。)
总结
解决主从一致性问题
如下图,如果按照下面方法来操作,获取锁时,如果redis的主节点宕机了,主从还未执行同步,而redis会从slave结点中选取作为主节点,但再次访问时会发现锁已经失效了
初始
宕机后,选取主节点
multiLock(联锁)
需要同时获取成功才算成功,避免了主机宕机而导致锁失效的问题