分布式锁
1.前沿
上几篇文章分别介绍了数据库里面的锁及java里面的锁,大概了解到锁就是为了数据的一致性,防止出现数据错乱,但是单机环境下用java的syn和lock来实现多线程异步执行的同步性,能
有效解决高并发的问题,但是实际中大公司往往为了实现系统的高可用,会选择分布式集群部署。因此如果单纯的靠syn和lock已经满足不了要求。
2.业务场景描述
业务场景1:譬如要实现一个排期的业务,需要往数据库表里的数据每天的0点执行插入一条数据,这时会用到定时任务,spring目前已经很好的集成了quartz,可以很方便的使用注解的形式来实现一个定时
任务,在单机环境下,很快就满足了任务,但是发现部署到生产环境,采用的是nginx集群部署分配,这时,两台机器在某一刻的时候,会同时执行定时任务代码,这时就出现了一个问题,任务重复执行,导致
业务出错,首先发布的项目在两台机器上,项目代码是一致的,那如何保证只有一台执行了。
解决办法1:在执行业务代码的时候,可以加入一张log表,在执行任务之前先判断下,是否有当天的log,一旦发现有了,就不执行,貌似能解决问题,但无法保证线程的执行顺序,可能还是会出现执行2次代码。
解决办法2:既然只要保证一台机器执行,是否可以指定其中的一台机器执行,譬如我可以判断部署机器的ip地址来执行,当然也有问题,譬如这台机器刚好挂了,那某一天的任务可能就不执行了。
解决办法3:直接把定时任务抽出一台机器,不管业务里面的代码逻辑,只管执行,可以将A和B的执行controller抽出,由定时任务工程C通过http请求调用nginx随机转发,返回一个状态码来判断是否成功。当然这里涉及部署的定时任务工程C也是单机部署,如果是集群部署,同样存在这个问题,因此需要引入分布式锁来解决问题。
业务场景2:要实现B2C网站一个支付的功能,卖家在平台出售商品,平台先临时保管资金,等卖家冻结资金释放之后,才能进行提现。这里首先考虑的是支付时的并发问题,即假设有好几个人同时在购买
某一个店铺下的商品,这时如果多线程访问支付,可能出现脏读,譬如资金余额原本是20,A买了30,B买了40,这时按理应该是20+30+40=90,但最终拿到的结果可能是50或60.另外假设卖家在卖货收入的同时,刚刚他准备去提现,这不能保证是一个有序进行的,很容易出现脏读。 因此也需要引入一个支付锁,来锁定账户来保证账户的一致性和安全性。
3.实现分布式锁的方案
1.基于数据库实现分布式锁
2.基于缓存(redis)实现分布式锁
3.基于Zookeeper实现分布式锁
4.分布式 局限性
分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可