分布式锁5种(分布式锁你用对了吗)

一、什么是分布式锁

分布式锁是控制分布式系统之间同步访问共享资源的一种机制。在单节点应用中,我们需要对一个共享资源进行多线程访问时,使用普通的锁就能够保证同步循序访问资源,不存在线程安全问题。当你掌握了流量密码后,业务突飞猛进,应用部署需要扩容,进行集群部署。此时,普通的锁已经控制不了集群各节点线程对共享资源的访问,为解决此问题,分布式锁诞生。

二、分布式锁特性与应用场景

  • CAP理论的理解CAP是分布式领域⾮常重要的⼀个理论,很多分布式中间件在实现时都需要遵守这个理论,其中:C表示⼀致性(Consistency):指的的是分布式系统中的数据的⼀致性。A表示可⽤性(Availability):表示分布式系统是否正常可⽤。P表示分区容错性(Partition tolerance):表示分布式系统出现⽹络问题时的容错性。CAP理论是指,在分布式系统中不能同时保证C和A,也就是说在分布式系统中要么保证CP,要么保证AP,也就是⼀致性和可⽤性只能取其⼀,如果想要数据的⼀致性,那么就需要损失系统的可⽤性,如果需要系统⾼可⽤,那么就要损失系统的数据⼀致性,特指强⼀致性。CAP理论太过严格,在实际⽣产环境中更多的是使⽤BASE理论,BASE理论是指分布式系统不需要保证数据的强⼀致,只要做到最终⼀致,也不需要保证⼀直可⽤,保证基本可⽤即可。
  • 分布式锁应具备的能力1、在分布式系统环境下,一个资源在同一时刻只能被一台机器的一个线程使用;2、高可用的获取锁与释放锁;3、高性能的获取锁与释放锁4、具备可重入特性;5、具备锁失效机制,防止死锁;6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。
  • 高频业务场景1、扣减库存2、订单支付3、退货退款这些业务场景是一定不能出问题的,特别是高并发,大流量的环境下,一但出问题,就需要对海量异常数据进行恢复,这是我们不希望发生的。
  • 分布式锁实现方式1、基于数据库实现分布式锁2、基于Redis缓存实现分布式锁3、基于Zookeeper实现分布式锁

三、分布式锁你用对了吗?

使用了分布式锁,为什么你的订单还是有重复支付的记录产生?

先来说故事,小Q已做开发好几年,近期负责一个新项目的订单支付接口开发。在小Q看来,支付API已有,毕竟也有几年工作经验,轻车熟路,知道订单支付要加分布式锁,信心满满,挥一挥手干完一个订单支付接口。系统测试阶段,支付功能正常,没啥BUG。系统上线后,订单支付功能运行良好。突然有一天,客服接到客户反馈"你们这是什么破系统,竟敢重复扣我的血汗钱"。小Q接到项目经理通知后,惊呆了,心想"难道是架构师提供的分布式锁组件有bug?"。

然而并不像小Q想的那样,原因竟是小Q自己的分布式锁使用不当造成的。原来架构师为了防止死锁,提供了一个可以设置锁超时自动释放的加锁方法。而小Q在加锁时也考虑到了死锁的情况,给加锁方法设置了一个10秒的超时释放时间。正常情况下,一个同步支付的请求处理时间不超过2秒,小Q考虑10秒总该够了吧,于是自信地设置了10秒。

小Q在业务处理异常考虑得较少,在支付处理中,支付后需要调用外部服务推送数据,推送数据接口由于异常15秒后报了ReadTimeout。而小Q加锁的代码中正包含调用外部服务推送数据方法。业务要求如果推送数据失败不能影响正常支付,所以小Q用了try..catch,记录失败日志以便后续系统定时任务重新推送。所以推送数据超时后也正常写入了支付数据。

由于用户支付操作等了很久,在12秒的时候又重新发起了一次支付,此时,由于第一次支付的锁释放时间(10秒)已到,锁自动释放。第二次支付请求,在12秒又获得锁,第二次支付也正常完成流程处理。产生了2笔都是支付完成的支付记录。

分布式锁5种(分布式锁你用对了吗)(1)

支付时序图

图中主要有几个问题:

  • 分布式锁失效时间设置不当为了防止死锁,可以合理使用使用失效机制。既然要设置失效时间,那么重点应该关注时间的长度。时间多少取决于锁从开始到结束整个过程中包围的业务代码处理的总时长。即:本地接口处理时间 外部接口处理时间。注意要考虑外部接口调用超时情况(一般会设置超时熔断)。所以锁的失效时间长度应该大于锁范围内的业务处理总耗时。
  • 业务流程编排不合理调用第三方支付正常响应后写入支付数据,此时支付业务已完成,可以提交事务。推送数据可以try...catch,说明数据强一致性要求不高,不需要包含在Transaction中。对于可以着延时的数据处理,推荐使用消息中间件处理,只要保证数据的最终一致性即可。
  • 事务作用范围不合理Transaction的使用,大家都明白要么都成功,要么都失败。但是还要提前考虑什么需要都成功,为你使用事务正确提供决策条件。另外要注意"要么都失败"的理解:不是不用管了,使用Transaction,最重要的是失败后怎么保证业务数据不丢失。在测试环境中,数据丢了可以重新造。生产环境中,数据丢了客户要给你投诉,你还得费尽精力恢复补数据,你还得背负生产责任,所以数据的最终一致性是必须的,这也是系统健壮稳定的一个评判标准。
  • 异常处理考虑不周做设计、开发、测试最容易忽略的就是异常,测试环境系统运行稳如狗,一到生产就趴窝。不同的环境,数据容量、网络架构、硬件配置、软件配置、访问流量、网络访问策略等等因素,都可以给我们参考,从而做出良好的设计,开发出健壮稳定的系统。
,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页