支付宝防并发方案之"一锁二判三更新"
每年支付宝在双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访问,为了保证用户的顺畅体验,牺牲了状态判断的安全性,在事后再做一次对账(虽然就算出错也于事无补了 :))
支付宝防并发方案之"一锁二判三更新"的更多相关文章
- php面试题二--解决网站大流量高并发方案(从url到硬盘来解决高并发方案总结)
php面试题二--解决网站大流量高并发方案(从url到硬盘来解决高并发方案总结) 一.总结 从外到内解决网站大流量高并发问题---从提交一个url开始(从用户按下搜索栏回车键开始) url最开始会到d ...
- 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念
深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.AQS框架简介 AQS诞生于Jdk1.5,在当时低效且功能单一的synchroni ...
- Java高并发情况下的锁机制优化
本文主要讲并行优化的几种方式, 其结构如下: 锁优化 减少锁的持有时间 例如避免给整个方法加锁 1 public synchronized void syncMethod(){ 2 othercode ...
- python并发编程之multiprocessing进程(二)
python的multiprocessing模块是用来创建多进程的,下面对multiprocessing总结一下使用记录. 系列文章 python并发编程之threading线程(一) python并 ...
- 并发编程从零开始(十二)-Lock与Condition
并发编程从零开始(十二)-Lock与Condition 8 Lock与Condition 8.1 互斥锁 8.1.1 锁的可重入性 "可重入锁"是指当一个线程调用 object.l ...
- Java线程并发中常见的锁
随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题.本文着重介绍了在java并发中常见的几种锁机制. 1.偏向锁 偏向锁是JDK1.6提出来的一种锁优化的机制.其核心的思想 ...
- 咏南IOCP中间件支持海量并发方案(集群)
咏南IOCP中间件支持海量并发方案(集群) 支持D7~XE10.1.1开发 支持负载均衡,自动故障转移 可以在不停机的状态下,根据负载情况灵活增加中间件机器 中间件使用IOCP通信,单中间件支持并发数 ...
- Java线程并发中常见的锁--自旋锁 偏向锁
随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题.本文着重介绍了在java并发中常见的几种锁机制. 1.偏向锁 偏向锁是JDK1.6提出来的一种锁优化的机制.其核心的思想 ...
- sql server对并发的处理-乐观锁和悲观锁
https://www.cnblogs.com/dengshaojun/p/3955826.html sql server对并发的处理-乐观锁和悲观锁 假如两个线程同时修改数据库同一条记录,就会导致后 ...
随机推荐
- 006-使用python编写一个猜数字的程序
题目:随机生成一个数字,共有三次机会对该数字进行猜测. #功能点# 1.猜错的时候给出提示,告诉用户输入的值是大了还是小了# 2.最多提供三次机会# 3.随机生成需要猜的数字答案 编写思路: 1.刚开 ...
- Ocelot(二)- 请求聚合
原文:Ocelot(二)- 请求聚合 Ocelot(二)- 请求聚合与负载均衡 作者:markjiang7m2 原文地址:https://www.cnblogs.com/markjiang7m2/p/ ...
- java-异常处理2
一 编译时异常和运行时异常的区别 java认为如果你的程序有问题,你应该让调用者知道. 例如:面包,长毛了.用户去买了,用户可能会挂 .应该在面包上贴上标签(异常). java 如果在函数内抛出Exc ...
- Leetcode653.Two Sum IV - Input is a BST两数之和4-输入BST
给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true. struct TreeNode { int val; struct TreeNode * ...
- leetcode 31-40 easy
38.Count and Say The count-and-say sequence is the sequence of integers with the first five terms as ...
- (转载)怎样解决SQL Server内存不断增加问题
在启用sqlserver服务后,发现进程sqlservr.exe的内存使用量从开始的100多MB持续增加,很快就高达1G以上,造成机器运行缓慢.卡机,严重影响使用.sql server 在查询大数据量 ...
- Effective Modern C++:08调整
41:针对可复制的形参,在移动成本低且一定会被复制的前提下,考虑将其按值传递 class Widget { public: void addName(const std::string& ne ...
- vs dump调试
1. dump文件和pdb文件的匹配问题 >> 发布二进制文件时生成的pdb文件一定要保留,只有当发布的二进制文件和pdb文件是同时生成的才好正确调试. 2. dump文件和pdb文件放在 ...
- XML配置里的Bean自动装配与Bean之间的关系
需要在<bean>的autowire属性里指定自动装配的模式 byType(根据类型自动装配) byName(根据名称自动装配) constructor(通过构造器自动装配) 名字须与属性 ...
- PAT天梯赛L1-002 打印漏斗
题目链接:点击打开链接 本题要求你写个程序把给定的符号打印成沙漏的形状.例如给定17个"*",要求按下列格式打印 ***** *** * *** ***** 所谓"沙漏形 ...