基于文件锁和redis的方案

   

情景1:订单A和订单B同事进行操作,同时进行了库存的操作,比如仓储系统的发货操作(减库存),因为A和B包含的产品有重叠的部分,由于mysql是允许并发查询的,所以A可能读到B改之前的库存数,在进行库存修改造成数据错误,尤其是计算期初和结余时(有人说用乐观锁加版本号的形式,但是一个订单操作往往是一个事物中完成,需要操作很多sku的库存最后才commit,如果因为一个sku库存更新失败而回滚整个事物,和最早开始的文加锁就没区别了,还不如不操作更节省资源,所以比较好的方式是排它锁),此时如果加入简单文件锁可以保证A操作时B无法操作,避免数据错误,但是B很可能会等待超时失败,加入订单很多,重复很多,其中有订单操作时间又长,锁一个库的方式势必会造成等待和大量订单失败。

所以一个是将锁颗粒变小,减小锁的范围(每个库多个锁),二是及时解锁解除无效占用。

这里锁要满足以下原则:

1.排它(互斥),A上锁的部分B不可操作

2.不能死锁,或者有解决死锁的方法

3.谁上的锁谁解,A不能解B的锁

 

一、文件锁可以保证互斥

 之前有个简单的文件锁方案,这次由于操作的单量比之前大很多,又要求速度,上锁就更精细了,不能只能到库。

   所以领导决定将每个库分成多份,比如100份,也就是100个锁,这样可以提高精细度,不至于一次锁一个库,其他人都操作不了。怎么实现呢,方案就是用“库Id+sku_id%100"作为文件名字,创建这个文件就锁住了所有sku在这个范围内的,

   但是这样就会产生一个问题,上锁颗粒度是小了,但是多个订单拥有的sku大概率会有有重叠的部分,比如

   订单1:sku:1   2  3  4  5  6  7  8  9

   订单2:sku:                 5  6  7   10  11  12

  订单3:sku: 5  6   7  15  19

。。。

如果订单1把 5 锁住的时候  订单2把6锁住了,订单1再拿6时会失败,订单2再拿5时也会失败,相互都拿不到对方的锁,又拥有对方的锁,造成死锁(互卡),订单3也是一样的情况。为了解决死锁问题,我决定使用redis

缓存解决。

 

本来我想的是只用redis作上锁,比如以"sku+库id"做为key(也可以互斥),这样可以让颗粒度更小,直接类似于mysql的行锁,但是老板说只依赖redis他不放心,万一redis挂了或者不好用了,他还是钟情于之前的文件锁,遇到问题到时候直接删文件就好了,并且由于文件就在项目服务器中,避免了跨服务器网络请求,会更快。我说其实redis可以做持久化和高可用的。但是为了让老板放(饭)心(碗),啥活也得接不是,那我们就做一个使用文件上锁,用redis解锁的方案。

大体思路就是:1、利用系统不允许同一路径下 同时创建相同名称的文件(这个不用解释吧,linux和windows都不行的),这就是锁,假如我们有两台linux服务器,在A服务器指定一个目录存放”锁文件",并将这个目录和另一个服务器共享,将B服务器同样位置的目录指向(映射,比如做NAS)A的目录,这样就实现两台服务器一个锁,可以实现分布式锁。2、利用redis做缓存,将上锁成功和失败的信息存储进去,然后根据信息做解锁处理,再删除信息,这种反复存储redis缓存的效率高而且是单线程(redis是串行处理命令的),可以省很多事(比如处理并发问题)。

那么流程图如下:

https://download.csdn.net/download/xiaobai1024/13211827

说明:中心思想就两个方法,1:上锁,2:解锁。主要逻辑在于上锁,上锁时会将成功和失败的订单信息和产品信息存入redis,然后利用redis中的信息进行判断解锁(解锁策略)。

二、不能死锁

从流程图上看,不死锁的方法有两个,一个是超时解锁,一个是及时解锁。

超时解锁不难理解,假如一个订单10个sku,当成功上了8个锁,失败的两个还要循环去请求上锁,也就是订单还在处理中的状态,但是不能无限等待下去,因为它自己也占了8个锁,所以会有超时时间 ,超过一定时间则会处理失败,释放掉它的锁,允许其他订单操作。这个策略不区分产生死锁还是没有得到锁。

所谓及时解锁其实是放弃加锁,在产生死锁时使用,因为要满足第三个原则(谁上的锁谁解),所以在加锁的方法里加入主动放弃的策略,即释放自己锁,本次加锁失败。但是如何判断是否放弃还是继续请求加锁呢,就要基于redis中的数据进行权重计算,1.谁加锁加的多谁有权利继续加锁,少的放弃,(加锁比例)2.比例相同,谁先加锁谁有权利继续。

通过以上两个策略避免了死锁,当A订单没有和B产生死锁,A会等待B处理完毕处理,若B时间过长,A会因超时放弃。多个订单也是一样道理。如果A和B在上锁时发生互卡,他们自己必须判断是否要放弃加锁,最终会有一方放弃,即使判断时有一个线程坏了(比如重启服务器),另一方也会因超时解锁。

那么源代码如下:

https://download.csdn.net/download/xiaobai1024/13634109

 

问题:

一。加入文件锁,代码稍显复杂。逻辑设计也复杂了。

二。如果线程挂起或者崩溃,那么会有文件锁残留,需要手动删除才能解除。

所以当时我是想直接用redis的,毕竟redis有设置超时时间的方式,超过时间可以自动失效,就不用考虑残留问题。但这不是问题,将方法稍加改造就能实现了,需要注意的是redis的高可用和持久化问题。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值