首先看下面这段代码:
这是一段抢券代码,看着没什么问题,但是当是多线程情况下,那么就可能出现并发问题,出现超卖。为了解决这个问题,我们可能会加锁,那么接下来看加锁后的代码:
这段代码已经完美解决了超卖的问题,但是我们要想,如果我们的项目是单体项目,且只启动了一台服务,那么上面加锁后的代码是没问题的。但我们的项目往往是集群部署,把同一份代码部署再多台服务器上。如下图所示:
如果是这样的话,那么我们加锁的逻辑就又出现问题了。看下面逻辑:
左边是 8080 服务线程,右边是 8081 服务线程。8080 服务线程 1 可以拿到互斥锁,查询到优惠券,而右边 8081 服务线程 1 也拿到互斥锁,查询了优惠券,这时我们发现各个服务都能查询到优惠券,这就出现了问题。
那这是为什么呢?这是因为Sychronized属于本地锁,目前锁是属于 JVM 的,然而每一个服务都有自己的 JVM。Sychronized只能解决同一个 JVM 线程下的互斥,而不能解决多个 JVM 下线程的互斥
。所以我们需要使用一个外部的锁,那么这就是分布式锁
。
Redis 实现分布式锁是基于 Redis 的 setnx
命令来实现的。
设置超时时间是为了防止某台服务宕机,能够自动释放锁。
如何合理控制锁的有效时长?换句话说,如果执行业务的时长大于了锁的时间,那么该怎么办呢?我们就需要给锁续期。我们可以新开一个线程监控,然后进而监听、续期。现在市面上有很多成熟的技术,那么 redisson 就是其中一种。
Redisson 实现分布式锁也是基于 Redis 的 setnx 命令来实现的,只不过 Redisson 又做了很多的增强和优化。
看下面流程:
加锁成功以后,会另开一个线程进行监控这个持有锁的线程,然后续期。releaseTime 就是锁的过期时间,默认是 30 秒。释放锁需要我们手动来释放,然后通知看门狗无需监听。
Redisson 中还新增了重试机制
当一个线程来了之后,不能获取到锁,会进行不断地循环获取锁。流程如下:
所有 redis 命令基于 lua 脚本完成的,保证原子性。
redisson 使用:
待补充~~~
RedLock 红锁可以保证主从一致性,但是实现复杂,性能差,较麻烦,所以不推荐使用。
https://www.bilibili.com/video/BV1yT411H7YK?p=14
https://www.bilibili.com/video/BV1yT411H7YK?p=15