让你设计一个分布式锁你怎么做

https://www.bilibili.com/video/BV1KF5LzzEsg
本文是看完视频后的自用总结,可能会有错误,欢迎评论区指出
如果觉得内容对你有用,可以点个赞,谢谢 =v=

让你设计一个分布式锁你怎么做

简单分布式锁

首先明确锁是什么,锁就是一个独占资源,拿到这个资源就可以进行操作,拿不到就不能往下走,这就是锁的互斥性

为什么需要分布式锁,是因为Java的锁有局限性,实际开发中往往会搭建服务集群,每个服务都会有自己独立的JVM, 本服务的锁对其它服务不可见

那怎么实现互斥呢?

加锁之后要解锁,解锁之前要判断是否为当前线程的锁,避免释放错锁;先判断再解锁,这是两步操作,为了保证原子性,需要放到 Lua 脚本里。

为什么 setnx 不需要放到 Lua脚本里呢,因为它是单个命令,Redis执行命令是单线程的,天然具备原子性。
那一个线程挂了怎么办?挂了他就无法释放锁了,所以得给锁加个过期时间,即便线程挂了,到时间也会自动释放掉锁。
那线程还没执行完时锁过期了怎么办?看门狗机制。开一个看门狗线程,定时给锁续期,保证业务执行期间锁不会被释放。那么问题来了,如果业务线程挂了,看门狗一直给续期怎么办?把看门狗设置为一个守护线程,这样它依赖的线程挂了之后,它也会随之停止运行。

可重入

开发中会用到递归,方法调用,就会出现同一把锁需要被同一个线程获取的情况,比如方法A和B都需要同一把锁,然后A调用B,这就需要锁是可重入的。那么如何实现锁的可重入呢?这里参考Java的synchronized和ReentrantLock,它们内部有一个锁计数器用于存档获取锁的情况,加锁就+1,解锁就-1。所以分布式锁想要可重入,也得有一个锁计数器。怎么搞?可以使用Redis的Hash结构,相当于Map<String, Map<String,String>
,比如以要锁的东西为key,线程ID-UUID为field(在容器化部署中,线程ID可能不稳定,可以选择直接用雪花算法之类来生成全局唯一标识),获取锁的次数为value。这也是Redisson的底层实现,用的是Redis的哈希结构。

为什么要用线程ID-UUID为field呢?还是因为集群,每个JVM生成的线程ID可能是会出现重复的,所以需要借助UUID保证锁的唯一性

锁阻塞

当线程没有获取到锁时并不会直接返回失败,会过一段时间再去抢锁,这就是所谓的阻塞锁。那么想想ReentrantLock是怎么实现阻塞锁的?首先尝试获取锁,获取锁失败后会包装为等待节点加入等待队列,然后自旋等待下一次获取机会。但是Redisson可重入锁不是这样设计的,而是使用了Redis的发布/订阅机制,没获取到锁的线程会订阅获取到锁的线程,获取到锁的线程执行结束后会发布一条消息,订阅了的线程就会被唤醒去抢锁,重复这个过程。当然也会设置一个超时时间防止无限等待。

联锁

如果为一主多从,主节点的锁还没同步到从节点,结果主节点挂了,那么就会有一个从节点去接手成为新的主节点,现在新的主节点是没有锁数据存在的。这时候服务集群都以为加锁成功了,但是由于新的主节点没有锁的数据,其他线程仍然可以加锁成功,这就是主从架构的锁丢失问题

怎么办呢,Redisson提出了一个联锁机制,要求Redis集群为多主或多主多从,每次加锁都需要给所有主节点都加上锁才算成功,这样即使有某个主节点宕机了,由于其他几个主节点有锁的数据,就能保证有新线程尝试加锁时因无法给所有主节点加锁而导致加锁失败。

那么就没有问题了吗?如果因为网络延迟或宕机导致一个主节点迟迟无法加锁成功,那么就会导致加锁失败率很高,而且失败后需要回滚其他主节点的加锁记录,性能太差。

红锁

Redisson提出红锁机制来解决联锁机制失败率过高的问题,现在不需要所有主节点都加锁才算成功,现在只需要有一半以上((总结点数/2) + 1))个节点加锁成功即可,它对加锁时间也有做限制,所以可以放弃慢节点的锁,这样即便有新线程尝试加锁,也会由于加锁节点数无法达到主节点数量的一半以上而导致加锁失败,这就天然保证了互斥性

这样就没有问题了吗?不是的,由于红锁对时间很严格,可能由于不同节点的系统时钟出现的不一致而出现问题。由于JVM的GC,他会STW导致所有线程暂停,看门狗无法对锁进行续期,然后锁就超时释放了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值