分布式锁的多套解决方案(演变篇)

目录

一、分布式锁的业务应用场景

1.1、用户下单业务流程图

1.2、超卖原因分析

  二、Mysql解决分布式锁 

2.1  解决方案   

2.2 性能分析

三、Redis实现分布式锁

3.1 解决方案带来的问题

1、如果上锁之后,JVM宕机,那么锁就会一直存在Redis中,造成死锁

2、基于问题1解决之后,会产生以下问题

3.2 性能分析

3.3 Redis集群问题

3.4 还有一个问题

四、Zookeeper实现分布式锁


一、分布式锁的业务应用场景

        1.1、用户下单业务流程图

        1.2、超卖原因分析             

1、多个用户同时对商品A进行下单
2、多个请求此时分别打到了多个订单服务中
3、多个订单服务对多个库存服务进行查询库存
4、此时多个库存服务同时查询数据库中的库存都为1
5、同时多个库存服务返回库存足够并扣减库存
6、所有用户下单成功,库存为-1,出现了超卖

  二、Mysql解决分布式锁 

        2.1  解决方案   

Mysql最常用的就是表(废话)。使用Myql可以根据商品的主键等唯一性的值作为标识,新建一个锁表

        2.2 性能分析

                以下图表数据是阿里云提供的对  MYSQL8.0 对阿里云的测试结果

             

基于MYSQL实现分布式锁,以下的几点我们需要权衡下

1、MYSQL的压测结果中,虽然取得不错的成绩;
但是在千万级并发的情况下使用MYSQL作为分布式锁的解决方案的情况下
硬件的设备需要有一定的要求支持,对企业的成本过高。

2、MYSQL是基于磁盘IO;
而分布式锁的应用场景是并发量较高的情况下发生,基于磁盘IO,我们更希望的是使用内存IO

经过上面的分析,我们希望锁的实现是基于内存,而且要快。那么我们第一时间会想到什么?

没错,那就是Redis!

三、Redis实现分布式锁

        Redis是一个开源,内存存储的数据结构服务器;

        可用作数据库,高速缓存和消息代理队列。具体学习可阅读以下文章:   
        Redis从零到进阶知识总结

        基于Redis中Setnx命令实现,如果上锁成功,则扣减。

        如果上锁失败,则上锁失败;返回下单失败

        

 3.1 解决方案带来的问题

        1、如果上锁之后,JVM宕机,那么锁就会一直存在Redis中,造成死锁

我们可以根据Setnx (setnx key ttl value)的命令参数设置一个过期时间,防止JVM宕机后造成的死锁问题

        2、基于问题1解决之后,会产生以下问题

         如果在上锁成功之后,并且设置了一个过期时间。此时服务A在执行业务逻辑中可能由于IO、网络的不稳定性、JVM突然的STW导致的时间超过了过期时间之后,继续执行业务逻辑的同时,另外的一个集群服务B进行上锁(此时Redis中的锁已过期,此时服务B进行上锁之后,服务A这个时候才执行文对应的业务逻辑,并且把锁删掉,会把服务B的锁删除!)

防止删除别人锁问题:
我们可以根据Key Value 键值对的Value值,每个服务每次上所产生一个唯一的标识
当释放锁的时候根据Key获取到的Value与本地记录的Value是否一致,不一致则不进行删除

防止Key过期:
我们可以使用Watch Dog(看门狗)解决;
有一个小狗狗专门负责监控哪个服务进行上锁;
如果到期时间到了之后还没有释放所,那么小狗狗将对这把锁进行续期,延长过期时间

3.2 性能分析

        上述使用Redis实现了分布式锁防止超卖

        但是如果在千万级并发的情况下,几千甚至几万个请求同时去Redis中抢一把锁

        可想而知,对性能指标来说是不达标的

分段锁

        分段锁的实现方案,是将一个商品的锁分成多个

        案例:

                假设商品A的库存有100件,此时我们可以将库存分成10份,每份10个单位。分别对应10

                个锁,这样将每个请求分发到不同的单位锁中。从原来的一把锁,拓展到了十把锁,

                性能可以提高十倍

        

3.3 Redis集群问题

        一般的生产环境上,Redis为了可用性肯定也是会做集群化,此时会引发一个新的问题。

        问题描述:

                如果说服务A在Redis中上锁Key1,Redis返回上锁成功后此时对Slave进行同步数据时

                Master挂掉了之后,选取出新的Master中,并没有原来Master中的Key1这把锁

                此时另外的服务进来就可以继续上锁,有可能引发超卖

        Redis红锁

                基于上述的Redis集群问题,我们可以使用红锁进行解决

                所谓红锁,就是部署多个独立的Redis,每次上锁都往这些Redis中上锁

                如果服务A上锁成功的数量超过Redis的一半,那么则为上锁成功!

                此时另外的服务B同时向这几台Redis上锁,如果超过一半以上Redis上锁失败

                则认定获取锁失败。

         红锁问题

                问题描述:

                        如果在服务A上锁后的过程中,其中一台的Redis挂掉了。运维人工马上又开启了 

                        一台新的Redis,此时五台Redis中只有两台Redis存在锁(不过半),那么另外的

                        服务B进行就可以上锁成功,就会可能产生超卖

3.4 还有一个问题

        在基于上面的Redis加上看门狗的机制下,万一此时的JVM内存满了产生STW

        那么看门狗也会无法正常监控,也有可能导致锁过期,导致另外的服务可以上锁

        从而产生超卖现象

那么基于上面的Redis解锁分布式锁的多个演变过程,可以得出以下几点结论

               1、针对于中小型企业来说,如果并发量不高,其实Redis已经可以解决锁问题

                2、Redis实现的红锁、看门狗、分段锁等解决方案虽然可以解决,但是实现的成本高

                3、基于红锁的问题和3.4提出的JVM内存产生STW,不能很好的解决分布式锁的问题

那么接下来,我们将会用最后一个存储方案解决分布式锁的问题,那就是Zookeeper!
                

四、Zookeeper实现分布式锁

Zookeeper是Apache开发维护的一个开源服务器,以实现高度可靠的分布式协调方案

根据ZK的两个特性

1、顺序节点存储

2、临时节点

基于ZK的两大特性,就可以很方便的解决以下问题防止超卖

1、顺序节点:
上锁后程序保存好的顺序节点值,在后续解锁时,如果跟保存的顺序节点值一样则断开会话连接(解锁)
防止删除另外程序的锁导致的超卖问题

2、临时节点:
临时节点的有效性是基于会话的。会话一旦结束,则自动解锁。并且如果在服务中产生STW
那么与ZK的会话连接也会断开,有效的防止了死锁的情况,也不需要使用看门狗机制防止锁过期

基于三种方案的分布式锁实现就先讲到这里

如果有疑问或者文章中有需要修正的

欢迎大家私聊 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值