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

星朝 星朝     2022-11-04     531

关键词:

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

诚然,为了实现在高并发下仍不会出错的技术目标,支付宝下了很多功夫,比如幂等性的处理,分布式事务的使用等等,但是个人觉得其中最关键的一点就是“一锁二判三更新”这句看似毫不起眼的口诀。

何为“一锁二判三更新”? 简单来说就是当任何一个并发请求过来的时候
1. 我们先锁定关联单据
2. 然后判断关联单据状态,是否之前已经更新过对应状态了
3. 如果基于第2步判断,之前并没有请求更新过对应状态,则本次请求可以更新并完成相关业务逻辑。
如果之前已经有更新过状态了,则本次不能更新,也不能完成业务逻辑。

示意图
技术分享图片

话不多说,我们直接上代码:

//第1步锁当前支付单PaymentInfo resultPaymentInfo = commonPayCoreService  .queryPaymentForUpdate(createPaymentInfo.getId());if (resultPaymentInfo.isFinalStatus())       //第2步,判断当前支付单状态,如果是终态,则直接返回       //不做任何更新      return resultPaymentInfo;//第3步更新当前支付单状态到终态,并完成相关业务逻辑(支付成功) payCoreService.updateRequestResult(payChannelResult);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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

如果第1步或第2步缺失了,会发生什么问题,我们来看一下:

第1步缺失
技术分享图片

第2步缺失
技术分享图片

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

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

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

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

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









蚂蚁金服-财富编码军规

...蚂蚁金服-财富编码军规无规矩不成方圆 ”01—基础编码并发控制,默认使用悲观锁,一锁二判三更新,乐观引入须谨慎。为防止数据并行写入造成状态回退或乱序,原则上使用悲观锁,先对需要操作的数据进... 查看详情

蚂蚁金服-财富编码军规

...蚂蚁金服-财富编码军规无规矩不成方圆 ”01—基础编码并发控制,默认使用悲观锁,一锁二判三更新,乐观引入须谨慎。为防止数据并行写入造成状态回退或乱序,原则上使用悲观锁,先对需要操作的数据进... 查看详情

技术风险规避方法

...客会同步迁移到微信公众号:程序猿小哥基础编码1.并发控制,默认使用悲观锁,一锁二判三更新,乐观引入须谨慎。2.幂等拦截,幂等新老要兼容,字段约束需一致,异常场景防击穿。3.状态推进,流转设计要完整,状态推进凭指令,业... 查看详情

为什么selectforupdate只在事务中起作用

...,防止数据出现问题。forupdate的使用场景如果遇到存在高并发并且对于数据的准确性很有要求的场景,是需要了解和使用forupdate的。比如涉及到金钱、库存等。一般这些操作都是很长一串并且是开启事务的。如果库存刚开始读的... 查看详情

写一个死锁

...入锁一需要锁二,另一个线程进入锁二需要锁一,由于锁一锁二都被占了,所以线程执行不下去。所以只需写一个相互交叉的锁一锁二就可以产生死锁。classsisuogouchengimplementsRunnable   privatebooleanpanduan=true;   sisuogoucheng(boolean... 查看详情

蚂蚁金服-财富编码军规

...蚂蚁金服-财富编码军规无规矩不成方圆 ”01—基础编码并发控制,默认使用悲观锁,一锁二判三更新,乐观引入须谨慎。为防止数据并行写入造成状态回退或乱序,原则上使用悲观锁,先对需要操作的数据进... 查看详情

资损分布式系统并发互斥设计

...公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。📫热衷分享,喜欢原创~关注我会给你带来一些不一样的认知和成长。🏆InfoQ签约作者、CSDN专家博主/后端... 查看详情

java高性能并发计数器之巅峰对决(代码片段)

java高性能并发计数器之巅峰对决并发计数器各个方案介绍方案概述jdk5提供的原子更新长整型类AtomicLongsynchronizedjdk8提供的LongAdder【单机推荐】Redisson分布式累加器【分布式推荐】方案介绍jdk5提供的原子更新长整型类AtomicLong在JDK1... 查看详情

[winform]-"更新dataset应用程序集对象失败,visualstudio自动重启"之解决

背景在WinForm解决方案中,更新DataSet应用程序集对象失败,VisualStudio自动重启。 试一试1.更新.xsd时打开对应的.Designer.cs。2.如果更新TableB失败,但是更新TableA没问题,试着同时更新AB两张表,TableB可能被附带着更新成功。3.失... 查看详情

怎么用java调用微信支付接口

java调用微信支付接口方法:RequestHandlerrequestHandler=newRequestHandler(super.getRequest(),super.getResponse());//获取token//两小时内有效,两小时后重新获取Token=requestHandler.GetToken();//更新token到应用中requestHandler.getTokenReal();System.out.println("微... 查看详情

微信支付宝个人收款解决方案之免签约支付解决方案之app监控通知方案(代码片段)

关键词:免签约支付,免签约收款,支付宝收款,微信收款,个人免签约收款背景​有做网赚或者在网上售卖个人小产品小服务的,需要进行收款,如果零星个别的收款可能直接走个人转账然后手动发... 查看详情

微信支付宝个人收款解决方案之免签约支付解决方案之app监控通知方案(代码片段)

关键词:免签约支付,免签约收款,支付宝收款,微信收款,个人免签约收款背景​有做网赚或者在网上售卖个人小产品小服务的,需要进行收款,如果零星个别的收款可能直接走个人转账然后手动发... 查看详情

支付宝个人收款解决方案之支付宝签约方案(代码片段)

关键词:签约支付,签约收款,支付宝收款,个人签约收款背景​有做网赚或者在网上售卖个人小产品小服务的,需要进行收款,如果零星个别的收款可能直接走个人转账然后手动发货即可,但是一旦... 查看详情

支付宝个人收款解决方案之支付宝签约方案(代码片段)

关键词:签约支付,签约收款,支付宝收款,个人签约收款背景​有做网赚或者在网上售卖个人小产品小服务的,需要进行收款,如果零星个别的收款可能直接走个人转账然后手动发货即可,但是一旦... 查看详情

支付宝个人收款解决方案之支付宝签约方案(代码片段)

关键词:签约支付,签约收款,支付宝收款,个人签约收款背景​有做网赚或者在网上售卖个人小产品小服务的,需要进行收款,如果零星个别的收款可能直接走个人转账然后手动发货即可,但是一旦... 查看详情

事务并发之隔离级别

...性和持久性(ACID)属性,只有这样才能成为一个事务。事务并发数据库是多个用户(事务)共享的,当多个用户同时访问数据时,那么在这种情况下就叫做并发。事务并发下可能出现的问题更新丢失两个事务都同时更新一行数据,一... 查看详情

高并发解决方案之actor——第一节

还在为状态的并发控制而痛苦吗? 还在因为数据库瓶颈而痛苦吗? 还在因为缓存的实时性控制而痛苦吗? 还在为了想分布式,但又不知道怎么下手而痛苦吗? Actor欢迎你!!! 一、什么是Actor? Actor提供... 查看详情

高并发场景之rabbitmq

高并发场景之RabbitMQ上次我们介绍了在单机、集群下高并发场景可以选择的一些方案,传送门:高并发场景之一般解决方案但是也发现了一些问题,比如集群下使用ConcurrentQueue或加锁都不能解决问题,后来采用Redis队列也不能完... 查看详情