关于分布式锁的一些思考

为什么要有分布式锁这个东西?

你有没有考虑过这样一个问题:既然已经有了Synchronized,ReentrantLock,AtomicXxx 等机制,为什么还需要分布式锁呢?不要急,听我慢慢道来。

“锁”概念的出现,其实是为了保证“线程安全”,而所谓的“线程安全”就是说:当多个线程同时执行并且对共享资源进行写操作的前提下,如何保证不出现脏数据。

那“锁”是如何保证线程安全的呢?很简单,就是进行“线程同步”,所谓的“线程同步”就是说一个线程一个线程来,只有A线程对文件写完之后,B线程才能再去写。

那么“锁”就有一个最基本的特点:范围。所谓的范围,就是说你这个锁能同步到的线程的范围

好,这就对应刚才的问题了,Synchronized的锁范围是什么呢?哦,如果放在A类的speak()静态方法上,锁的范围就是针对所有调用A.speak()的线程;如果是放在B类的say()非静态方法上,锁的范围就是针对所有调用B创建出来的某个b对象的b.say()的线程.......但是总而言之,Synchronized的最大范围逃不出一个JVM!最多最多Synchronized能同步的所有线程都在当前JVM中!

那么分布式锁是什么?为什么需要分布式锁?答案应该不言而喻了吧。

为了保证基本的高可用,我们的任何一个服务至少都得有两个实例(一个实例就是一个JVM)。现在如果我的业务是需要保证某个操作在整个服务中进行线程同步,那么必须使用分布式锁才能实现。如下图所示,红色是synchronized锁的同步范围,而黄色是分布式锁的同步范围。

分布式锁使用场景:

库存扣减:

比如现在要对库存进行扣减,多个线程都需要去对数据库里面的某条记录进行“减”操作,这个时候为了防止超卖的情况发生,必须得一个线程一个线程地执行,而且每个线程执行之前都需要查看一下当前库存数量是否大于等于订单中的数量。查看校验+扣减 两个操作需要在同步块内。

接口幂等:

什么是幂等性?就是一个接口调用一个和调用多次对数据库的影响是一致的。比如:你写了一个新增订单的接口,调用端因为RPC重试等原因多次调用了你的接口,你不能说你就真的去数据库中新增了多个信息一模一样的订单,这是不对的,所以要保证这个新增订单接口的幂等性。

而要保证接口幂等,一般需要在执行之前,先查一遍看看是否已存在或是否已操作过了。查看验证+执行 两个操作需要在同步块内。

其他场景:

总结一下,什么样的场景一般需要分布式锁呢?拿日常的Mysql数据库来举例,一条写SQL就可以解决的不需要分布式锁,因为Mysql内部的行锁、间隙锁等已经给你做好线程同步了!对于一条写SQL可以解决但是需要一些读SQL来做校验的,对于多条写SQL才可以解决的,都需要使用分布式锁!

 如何去设计一个分布式锁?

也就说分布式锁的实现机制大概是怎样的?如果让你来设计一个分布式锁要考虑哪些因素?

我们可以参考一下ZooKeeper的锁实现机制:

获取锁:A客户端连接ZK后,创建了第一个临时子节点,判断自己是第一个子节点后;

锁等待:B客户端连接上来ZK之后,创建了第二个临时子节点,判断自己不是第一个子节点,获取锁失败,监听自己的前一个节点;

锁释放:当A客户端处理完相关事情后与ZK断开连接,对应的第一个临时子节点消失。第二个子节点监听到该事件,重新去判断自己现在是不是第一个子节点,判断是则获取锁成功。

再大概参考一下Redis的锁实现机制: 

获取锁:A客户端连接Redis后,如果lock_key不存在,那么就去set lock_key,并设置过期时间,然后后台的watch dog会定期去延长lock_key的生存时间。

锁等待:B客户端连接Redis后,判断lock_key已经存在了,那么就获取lock_key当前的生存时间,然后不断轮询来查看lock_key是否已过期。

锁释放:A客户端处理完任务后,del lock_key。此时B客户端来轮询查看时,发现lock_key不存在,则走获取锁流程。

发现没有,所谓的分布式锁其实并不神秘,不过就是找一个所有服务实例中的线程都可以访问到的“公共存储”,然后看谁能做成一件事,做成的线程就获取了锁,做不成的线程就通过监听、轮询或是其他方式来等待锁,等待锁释放后重新尝试获取。整个过程中的“锁获取”、“锁等待”、“锁释放”都是抽象出来的概念!而最终的目的不过就是为了对线程进行同步!那么所谓的分布式锁的实现机制可以有很多很多,针对不同的中间件都可以来一套分布式锁实现机制!

这只是对于分布式锁的一个概念印象,而具体分布式锁在设计过程中要考虑哪些因素才是更重要的。我把我能想到的罗列在下面:

根据什么样的机制来判断获取了锁?有什么标记来证明锁是你的?

如何保证获取锁后在任务执行过程中锁不会丢失?

获取锁的客户端如果挂了应该怎么办?会不会导致锁一直无法释放?

锁获取失败之后应该用什么样的机制来等待锁,以便当锁释放后重新去获取?

根据什么样的机制来释放锁?如果保证只能自己才能释放?

如何保证锁重入?

如何实现公平锁、非公平锁?如何实现读写锁?

记住:问题很重要,而答案往往自然就会想得到。我们的主题是分布式锁,就不要拘泥于某种实现方式,也不要拘泥于各种实现方式之间的对比,直接抓住分布式锁的核心。设计一个分布式锁其实也没有那么难,对吧?


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值