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

每年支付宝在双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访问,为了保证用户的顺畅体验,牺牲了状态判断的安全性,在事后再做一次对账(虽然就算出错也于事无补了 :))

原文地址:https://www.cnblogs.com/jpfss/p/8890421.html

时间: 2024-10-10 03:44:37

支付宝防并发方案之"一锁二判三更新"的相关文章

基于Actor的并发方案

共享可变状态的问题 Actor模型 Actor系统 定义Actor 消息处理 副作用 非类型化 异步和非阻塞 创建一个Actor 发送消息 消息应答 问询Ask机制 有状态的Actor 小结 译者注: 本文原文标题:<The Neophyte's Guide to Scala Part 14: The Actor Approach to Concurrency>,作者:Daniel Westheide, 原文链接:http://danielwestheide.com/blog/2013/02/

加锁并发算法 vs 无锁并发算法

Heinz Kabutz 在上周举办了一次成功 JCrete研讨会,我在会上参加了对一种新的 StampedLock(于JSR166中 引入) 进行的评审.StampedLock (邮戳锁) 旨在解决系统中共享资源的争用问题.在一个系统中,如果多个需要读写某一共享状态的程序并发访问这个共享对象时,争用问题就产生了.在设计 上,StampedLock 试图通过一种“乐观读取”的方式来减小系统开销,从而提供比 ReentrantReadWriteLock(重入读写锁) 更好的性能. 在评审过程中,我

最新微信域名检测、防封,微信跳转技术揭秘(一) -- 域名检测原理及防封方案

背景 最近因为业务需要,在研究微信跳转,域名防封检测等东西,网上搜集了很多很多资料,发现居然这么简单的一点东西 居然有人专门做成系统拿去卖钱.. 系统功能就只是个微信跳转而已,微信跳外部浏览器  或者浏览器跳到微信内..  而且搜出来很多家这种收费的系统,界面都一模一样.. 真的是无语了.听说这还属于灰产... 我把这些弄出来是不是也可以拿去卖钱了?哈哈哈. 在网上搜索了很多相关的资料和帖子,发现很多都是大同小异的,原理其实都差不多, 但是搜了很多,它就是不把关键代码和原理告诉你... 也是,告

Java并发编程:Concurrent锁机制解析

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd

Objective-C中不同方式实现锁(二)-11-多线程

1 Objective-C中不同方式实现锁(二) 2 3 在上一文中,我们已经讨论过用Objective-C锁几种实现(跳转地址),也用代码实际的演示了如何通过构建一个互斥锁来实现多线程的资源共享及线程安全,今天我们继续讨论锁的一些高级用法. 4 5 1.NSRecursiveLock递归锁 6 7 平时我们在代码中使用锁的时候,最容易犯的一个错误就是造成死锁,而容易造成死锁的一种情形就是在递归或循环中,如下代码: 8 9 1 10 2 11 3 12 4 13 5 14 6 15 7 16 8

网狐游戏平台开发,防攻击方案定做。

了解防攻击方案具体可联系Q939886985. 我们的防攻击优势,一般每月5万元服务器费就可以足够顶单台20万每月的服务器.市面上真正单防200G防御服务器,至少是每月20万元.也有叫几万的,如果你相信有200G,那我也没有办法,多个成功大平台的运营者告诉我们,防攻击方案是最有效的,也是最节约的资金的. 多年的开发经验,维护经验,让你免受运营的烦恼.

利用基于控制器的加密方案进行数据保护(二)

利用基于控制器的加密方案进行数据保护(二) FIPS 140-2验证级别及需求 本加密系列的第一篇博文解释了基于控制器的加密(CBE),并概要介绍了FIPS验证流程.现在来探讨一下Federal Information Processing Standards 140 (FIPS 140-2,联邦信息处理标准)的验证级别及其需求. FIPS 140-2验证级别 与加密模块的设计与实现相关的领域共有十一个,其中每个领域的安全级别可以划分为1(最低)到4(最高)不等. 加密模块还有一个总安全级别的评

利用索引降低并发事务引起的锁【转】

时常,来自不同连接的线程会对同一张表进行读/更新操作,这种并发操作会导致阻塞,同时SQL Server会自动处理以防止脏读.然而,有种情景很常见,那就是每个连接要读/更新的行互相排斥,换句话说,就是各个连接读/更新的行没有交集.在这片文章中,将像大家展示如何恰当地使用索引来降低阻塞的发生,以便多个读/更新能够同时操作同一张表. 创建TEST表如下: SET ANSI_NULLS ON GO   SET QUOTED_IDENTIFIER ON GO   CREATE TABLE [dbo].[T

Java线程并发中常见的锁--自旋锁 偏向锁

随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题.本文着重介绍了在java并发中常见的几种锁机制. 1.偏向锁 偏向锁是JDK1.6提出来的一种锁优化的机制.其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作.也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约了操作时间,如果在此之间有其他的线程进行了锁请求,则锁退出偏向模式.在JVM中使用-XX:+UseBiasedLocking pac