支付宝防并发方案之"一锁二判三更新"

每一年支付宝在双11和双12的活动中,都展现了绝佳的技术能力。这个能力不但体如今处理高TPS量的访问,更体如今几乎不会出错,不会出现重复支付的状况,那这个是怎么作到的呢?java

诚然,为了实如今高并发下仍不会出错的技术目标,支付宝下了不少功夫,好比幂等性的处理,分布式事务的使用等等,可是我的以为其中最关键的一点就是“一锁二判三更新”这句看似绝不起眼的口诀。数据库

何为“一锁二判三更新”? 简单来讲就是当任何一个并发请求过来的时候安全

  1. 咱们先锁定关联单据
  2. 而后判断关联单据状态,是否以前已经更新过对应状态了
  3. 若是基于第2步判断,以前并无请求更新过对应状态,则本次请求能够更新并完成相关业务逻辑。 若是以前已经有更新过状态了,则本次不能更新,也不能完成业务逻辑。

示意图服务器

话很少说,咱们直接上代码:架构

//第1步锁当前支付单
PaymentInfo resultPaymentInfo = commonPayCoreService
  .queryPaymentForUpdate(createPaymentInfo.getId());
if (resultPaymentInfo.isFinalStatus()) {
      //第2步,判断当前支付单状态,若是是终态,则直接返回
       //不作任何更新
      return resultPaymentInfo;
}
//第3步更新当前支付单状态到终态,并完成相关业务逻辑(支付成功)
 payCoreService.updateRequestResult(payChannelResult);
复制代码

基于以上方案能够100%确保在并发状况下不会出现重复更新问题,按理论来讲,就是每次状态机变动前,都要在并发安全状况下判断状态是否已经发生过变动了。并发

若是第1步或第2步缺失了,会发生什么问题,咱们来看一下:异步

第1步缺失分布式

第2步缺失 ![无第2步流程.png] 高并发

只要把这3步做为咱们的代码规范,则能够避免大部分的并发重复操做问题。对于异步并发重复消息的处理亦是如此,加深对状态机的判断后还能够处理消息乱序问题。性能

对于锁的使用可根据实际状况选择[悲观锁和乐观锁]。 关于悲观锁(数据库行锁),乐观锁(数据库版本锁或分布式锁)的实现方式和坑咱们之后再详细说。

可能有人会问无论是悲观锁仍是乐观锁对系统的并发量都是有影响的,这个怎么解决?个人观点是在现代分布式系统中,若是追求高可用和稳定则必须在方案上优先知足,对于性能能够经过优化代码逻辑,优化技术架构,扩展数据库资源等方式来解决。

在以前蚂蚁金服的压测中,我负责的结算系统内部有10次左右SQL调用以及一次远程调用(约花费100ms),总流程花费180ms左右。在一台4核8G的机器上压测,java服务并发能够达到150TPS,结果仍是使人满意的,经过水平服务器扩展彻底没有问题。

在整个支付宝技术架构中,只有一个场景是没有用锁和判断直接更新的,就是2016年的春节五福红包,高达上百万的TPS访问,为了保证用户的顺畅体验,牺牲了状态判断的安全性,在过后再作一次对帐(虽然就算出错也于事无补了 :))

相关文章
相关标签/搜索