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

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

  1. php面试题二--解决网站大流量高并发方案(从url到硬盘来解决高并发方案总结)

    php面试题二--解决网站大流量高并发方案(从url到硬盘来解决高并发方案总结) 一.总结 从外到内解决网站大流量高并发问题---从提交一个url开始(从用户按下搜索栏回车键开始) url最开始会到d ...

  2. 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.AQS框架简介 AQS诞生于Jdk1.5,在当时低效且功能单一的synchroni ...

  3. Java高并发情况下的锁机制优化

    本文主要讲并行优化的几种方式, 其结构如下: 锁优化 减少锁的持有时间 例如避免给整个方法加锁 1 public synchronized void syncMethod(){ 2 othercode ...

  4. python并发编程之multiprocessing进程(二)

    python的multiprocessing模块是用来创建多进程的,下面对multiprocessing总结一下使用记录. 系列文章 python并发编程之threading线程(一) python并 ...

  5. 并发编程从零开始(十二)-Lock与Condition

    并发编程从零开始(十二)-Lock与Condition 8 Lock与Condition 8.1 互斥锁 8.1.1 锁的可重入性 "可重入锁"是指当一个线程调用 object.l ...

  6. Java线程并发中常见的锁

    随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题.本文着重介绍了在java并发中常见的几种锁机制. 1.偏向锁 偏向锁是JDK1.6提出来的一种锁优化的机制.其核心的思想 ...

  7. 咏南IOCP中间件支持海量并发方案(集群)

    咏南IOCP中间件支持海量并发方案(集群) 支持D7~XE10.1.1开发 支持负载均衡,自动故障转移 可以在不停机的状态下,根据负载情况灵活增加中间件机器 中间件使用IOCP通信,单中间件支持并发数 ...

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

    随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题.本文着重介绍了在java并发中常见的几种锁机制. 1.偏向锁 偏向锁是JDK1.6提出来的一种锁优化的机制.其核心的思想 ...

  9. sql server对并发的处理-乐观锁和悲观锁

    https://www.cnblogs.com/dengshaojun/p/3955826.html sql server对并发的处理-乐观锁和悲观锁 假如两个线程同时修改数据库同一条记录,就会导致后 ...

随机推荐

  1. CSS 教程 - 闭合浮动元素

    按照CSS规范,浮动元素(floats)会被移出文档流,不会影响到块状盒子的布局而只会影响内联盒子(通常是文本)的排列. 因此当其高度超出包含容器时,一般父容器不会自动伸长以闭合浮动元素. 但是有时我 ...

  2. mysql创建数据库指定utf8编码

    CREATE DATABASE IF NOT EXISTS dbname DEFAULT CHARSET utf8;

  3. bzoj3064/洛谷P4314 CPU监控【线段树】

    好,长草博客被催更了[?] 我感觉这题完全可以当作线段树3 线段树2考加法和乘法标记的下放顺序,这道题更丧心病狂[?] 很多人可能跟我一样,刚看到这道题秒出思路:打一个当前最大值一个历史最大值不就完事 ...

  4. 设置Linux系统的空闲等待时间TMOUT的方法和Linux反空闲设置的两种方法

    为了增强linux系统的安全性,我们需要在用户输入空闲一段时间后自动断开,这个操作可以由设置TMOUT值来实现.将以下字段加入到/etc/profile 中即可(对所有用户生效). export TM ...

  5. Djangog写XXOO管理的要求以及思路

  6. cmd 运行 python

    ①cmd 进入行命令: ②输入 “python” + “空格”,即 ”python “:将已经写好的脚本文件拖拽到当前光标位置,然后敲回车运行即可.

  7. hdu 1251 统计难题(trie树入门)

    统计难题 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131070/65535 K (Java/Others)Total Submi ...

  8. Minimal coverage (贪心,最小覆盖)

    题目大意:先确定一个M, 然后输入多组线段的左端和右端的端点坐标,然后让你求出来在所给的线段中能够 把[0, M] 区域完全覆盖完的最少需要的线段数,并输出这些线段的左右端点坐标. 思路分析: 线段区 ...

  9. Direct2D 第3篇 绘制文字

    原文:Direct2D 第3篇 绘制文字 #include <windows.h> #include <d2d1.h> #include <d2d1helper.h> ...

  10. Liferay Model Hints

    这篇文章主要介绍如何通过model hint 来修改liferay builder service生成数据库表中默认字段的长度. 1.什么是Model Hints? 它是对liferay builde ...