一篇文章带你搞定分布式锁常见问题
分布式锁
基本的分布式锁实现
基于zookeeper,redis
例如基于redis:
1)setnx,设置锁,设置过期时间,保存线程标识
2)释放锁的时候要先判断锁是否为当前线程持有的,需要用lua脚本来保证解锁的原子性
释放错锁的问题
1)拿到锁,判断锁是不是自己的,释放锁,这三步并不具备原子性,所以需要写在lua脚本里
如何实现Redisson可重入锁
由于自己写的锁使用了string的setnx,只要方法1上锁,方法1调用的方法二就没法再上锁,会直接返回false。
为啥?
redisson的可重入锁,使用了hash结构,key为lock, field为线程名,value为数字
- 加锁
- 方法一加锁后,value=1,方法一调用方法二
- 方法二加锁,Redisson会判断field是否为同一线程,若是,value++,value为2
- 同理…
- 解锁
- 每个方法执行完毕时依次value–
- 直到value为0,此时会删除redis的该数据,锁完全释放
如何实现锁的可重试/Redisson实现阻塞锁
redis的发布订阅机制实现 等待,唤醒,获取锁失败的重试机制
- 获取锁失败不是直接重试or返回,而是订阅一下,然后等待
- 当获取锁成功的线程释放锁后,会发布一条消息
- 其他线程得到该消息,就会去重新获取锁,获取失败,则继续等待
- 但也不是无限等待,超过一定时间,它就不会继续等而是返回false
redis的pub/sub只实现广播机制
业务没执行完成,锁过期了咋办/锁的续约机制
WatchDog机制来实现锁的续期,每隔一段时间,重置锁的超时时间
- 会创建一个守护线程,当锁快到期但是业务线程没执行完时为锁增加时间
- 当然也不是无限加,会有个上限
锁的主从一致性问题
redis为了保证高可用,会使用主从架构。
会出现以下问题
- 获取锁时,往redis主节点setnx。主节点加锁后宕机,没更新到从节点
- 某个从节点成为主节点后,但是新主节点没有锁信息。此时其他线程还是可以加锁
解决方案:Redisson联锁方案,使用多主or多主多从
redLock红锁
联锁方案还存在问题
- 所有节点都得上锁,才能获取锁成功。那么某个节点网络原因,就会导致加锁时间长,加锁失败
- 如果主节点宕机,也会加锁失败
使用redLock,它规定只要半数以上加锁成功,就算加锁成功。并且严格规定了加锁时间,该段时间内加锁失败,直接返回。
缺点:部署要求高,多主部署,不推荐哈