避免重复扣款:分布式支付系统的幂等性原理与实践

这是《百图解码支付系统设计与实现》专栏系列文章中的第(6)篇。

本文主要讲清楚什么是幂等性原理,在支付系统中的重要应用,业务幂等、全部幂等这些不同的幂等方案选型带来的收益和复杂度权衡,幂等击穿场景及可能的严重后果。

这也是支付公司面试的必考题目之一。

1. 什么是幂等性原理

幂等性是一个数学和计算机科学术语,用于描述无论操作执行多少次,都产生相同结果的属性。在软件行业,应用极其广泛,当我们说一个接口支持幂等时,无论调用多少次,对系统造成的结果是一致的

注意这里说的“对系统造成的结果是一致的”是指系统内部数据或状态的变更,不是指返回值。不同的系统设计,返回值可能是不一样的。

举个例子,你在淘宝免密支付10元,淘宝针对这笔订单调用支付宝支付接口进行支付,无论是调用1次,还是调用100次,最终只扣了你10元。但是第二次有可能返回“重复请求”,也有可能返回“支付成功”,这个取决于接口设计。也就是支付宝内部只扣了你10元,但是接口可能返回给商户是是不同的结果。

我个人倾向于方案一,如果等幂等,就返回:重复请求。减少误解,虽然两种方案中系统都只扣了一次钱。

2. 为什么幂等性在支付系统中极其重要

支付系统必须以最高的可靠性和准确性处理交易,这对于用户信任至关重要。如果一个支付系统不能保证幂等性,可能会导致多次扣除同一笔费用,引发用户不满和法律责任,严重时就会有舆情风险,甚至会被吊销牌照。

一般情况下,支付系统的幂等性能力要求比电商系统要求更高,如果用户在电商下单多了,只要没有支付,用户还是可以忍受的,但一旦多扣了用户的钱,后果就会比较严重。

这也是为什么幂等性会是支付系统招人的面试必考题目之一。

3. 支付系统中应用幂等性的场景

幂等是针对重复请求的,支付系统一般会面临以下几个重复请求的场景:

  1. 用户多次点击支付按钮:在网络较差或系统过载情况下,用户由于不确定交易是否完成而重复点击。
  2. 自动重试机制:系统在超时或失败时重试请求,可能导致同一支付多次尝试。
  3. 网络数据包重复:数据包在网络传输过程中,复制出了多份,导致支付平台收到多次一模一样的请求。
  4. 异常恢复:在系统升级或崩溃后,未决事务需要根据已有记录恢复和完成。内部系统重发操作。

4. 幂等解决方案

4.1. 业务幂等

所谓业务幂等,就是由各域自己把唯一性的交易ID作为数据库唯一索引,这样可以保证不会重复处理。

在数据库前面可以加一层缓存来提高性能,但是缓存只用于查询,查到数据认为就返回幂等成功,但是但不到,需要尝试插入数据库,插入成功后再刷新数据到缓存。

为什么要使用数据库的唯一索引做为兜底,是因为缓存是可能失效的。

在面临时经常有同学只回答到“使用redis分布式锁来实现幂等”,这是不对的。因为缓存有可能失效分布式锁只是用于防并发操作的一种手段,无法根本性解决幂等问题,幂等一定是依赖数据库的唯一索引解决。

大部分简单的支付系统只要有业务幂等基本也够用了。

4.2. 通用幂等组件

每个域都要做幂等处理,那就单独出一个独立的幂等组件,各子业务系统通过引用这个公共JAR包解决。

适用场景:应用部署不太多的时候。如果应用非常多,独立幂等DB的连接池就不够用。

这个时候,可以把幂等组件的代码共用,但是幂等数据库表使用业务系统的DB资源。解决独立幂等DB导致的连接数不够用的场景。

4.3. 通用幂等服务

解决DB连接数不够用的第二个解决方案:幂等组件服务化。这样的坏处就是复杂度和耗时都会增加。

4.4. 全局幂等

在多机房部署情况下,需要解决机房之间的幂等服务。这就使用到了全局幂等概念。

所谓全局幂等,就是多个机房共用一份幂等数据,这里面涉及的技术比较复杂,后面单独开一个章节讲。除了极少数全球部署的多活支付系统都用不上。

4.5. 通用幂等数据库表设计

核心字段:

uniqueKey:幂等主键,由各应用自定义,需要保证全局唯一性使用这个uniqueKey做hash后分库分表。比如商户的收单ID,上游的ID等。

appName: 应用名称,比如收单,支付等。

siteId:站点ID

extInfoMap:扩展字段,由各应用自定义,比如保存我方单号。

4.6. 方案选型建议

简单的支付系统,只需要使用业务幂等就够。

中型的支付系统,推荐使用通用幂等组件。这样方便运维。

全局幂等方案只有极少数公司会考虑。

5. 分布式场景下实现幂等性的挑战及应对

分布式支付系统面临的幂等性挑战核心有两点:

  1. 如何保证分布于不同地理位置数据中心的系统数据的一致性。
  2. 幂等数据和业务数据跨库事务一致性。比如幂等已经入库成功,但是业务数据库入库失败。

为了解决这些挑战,可以采取以下解决方案:

  1. 使用全局唯一的交易ID,跟踪每次支付请求,防止重复处理。
  2. 幂等住了之后,还需要继续查询业务数据,如果查询失败,仍然执行业务操作。
  3. 构建强大的状态机推进能力,严格定义事务各个状态的转换。
  4. 幂等服务的高可靠性。

6. 幂等被击穿场景及可能的严重后果

尽管有了上述措施,幂等性仍然可能因为以下原因失效:

  1. 在分布式系统中,由于同步延迟,导致多个节点未能即时识别重复请求。
  2. 请求流量切换。原本应该路由A机房的数据路由到了B机房,但是B机房的幂等数据缺失。
  3. 生成全局唯一ID的算法出现故障或人为变更,同一笔业务可能出现了2个业务ID。

在支付系统中,只要幂等被击穿,基本上都会出现资损事件。有时候是用户资损,有时候是平台资损。曾经碰到一个真实案例,上游域把某个幂等字段组成规则的取值变了,但是下游不知道,导致下游幂等失败,对同一笔业务处理了2次,直接资损数十万美金。

7. 结束语

幂等性是分布式支付系统的基本要求,对于确保交易的正确性和避免重复扣费至关重要。除开支付系统外,很多互联网应用基本上都需要有幂等能力。

有机会再单独讲讲全局幂等。

传送门

支付系统设计与实现是一个专业性非常强的领域,里面涉及到的很多设计思路和理论也可以应用到其它行业的软件设计中,比如幂等性,加解密,领域设计思想,状态机设计等。

在《百图解码支付系统设计与实现》的知识宇宙,每一篇深入浅出的文章都是一颗既独立但又彼此强关联的星球,有必要提供一个传送门以便让大家即刻到达想要了解的文章。

专栏地址百图解码支付系统设计与实现

领域相关

基本概念与概要设计:跟着图走,学支付:在线支付系统设计的图解教程

收单结算设计:支付交易的三重奏:收单、结算与拒付在支付系统中的协奏曲

技术专题

与数据库自增ID不同的业务ID:交易流水号的艺术:掌握支付系统的业务ID生成指南

签名验签:揭密支付安全:为什么你的交易无法被篡改

加密解密:金融密语:揭秘支付系统的加解密艺术

日志格式设计规范:支付系统日志设计完全指南:构建高效监控和问题排查体系的关键基石

幂等性设计:避免重复扣款:分布式支付系统的幂等性原理与实践

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/607622.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

k8s源码阅读环境配置

源码阅读环境配置 k8s代码的阅读可以让我们更加深刻的理解k8s各组件的工作原理,同时提升我们Go编程能力。 IDE使用Goland,代码阅读环境需要进行如下配置: 从github上下载代码:https://github.com/kubernetes/kubernetes在GOPATH目…

CTF-PWN-沙箱逃脱-【seccomp和prtcl-2】

文章目录 沙箱逃脱prtcl题HITCON CTF 2017 Quals Impeccable Artifactflag文件对应prctl函数检查源码思路exp 沙箱逃脱prtcl题 HITCON CTF 2017 Quals Impeccable Artifact flag文件 此时的flag文件在本文件夹建一个即可 此时的我设置的flag为 对应prctl函数 第一条是禁止…

JavaScript解构赋值完全手册

🧑‍🎓 个人主页:《爱蹦跶的大A阿》 🔥当前正在更新专栏:《VUE》 、《JavaScript保姆级教程》、《krpano》 ​ 目录 ✨ 前言 第一节:解构赋值的基本用法 第二节:对象解构赋值 第三节:数组解构赋值 第四节:参数…

Qt基础-QtGlobal常用的全局函数及随机数产生实例

目录 一、全局函数定义 二、应用上面的函数产生一个随机数 三、随机数实例

java8 Stream()流 list转map

List<User> list new ArrayList<>();User user1 new User();user1.setUserId("1");user1.setUserName("李四1");list.add(user1);User user2 new User();user2.setUserId("2");user2.setUserName("李四2");list.add(us…

Fluids —— MicroSolvers DOP

目录 Gas SubStep —— 重复执行对应的子步 Switch Solver —— 切换解算器 Gas Attribute Swap —— 交换、复制或移动几何体属性 Gas Intermittent Solve —— 固定时间间隔计算子解算器 Gas External Forces —— 计算外部力并更新速度或速度场 Gas Particle Separate…

【linux】tcpdump 使用

tcpdump 是一个强大的网络分析工具&#xff0c;可以在 UNIX 和类 UNIX 系统上使用&#xff0c;用于捕获和分析网络流量。它允许用户截取和显示发送或接收过网络的 TCP/IP 和其他数据包。 一、安装 tcpdump 通常是默认安装在大多数 Linux 发行版中的。如果未安装&#xff0c;可…

竞赛保研 基于深度学习的人脸表情识别

文章目录 0 前言1 技术介绍1.1 技术概括1.2 目前表情识别实现技术 2 实现效果3 深度学习表情识别实现过程3.1 网络架构3.2 数据3.3 实现流程3.4 部分实现代码 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的人脸表情识别 该项目较…

2023年12 月电子学会Python等级考试试卷(六级)答案解析

青少年软件编程(Python)等级考试试卷(六级) 分数:100 题数:38 一、单选题(共25题,共50分) 1. 运行以下程序,输出的结果是?( ) class A(): def __init__(self,x): self.x=x+1 def b(self): return self.x*self.x t=A(3) print(t.b())

Baumer工业相机堡盟工业相机如何联合NEOAPI SDK和OpenCV实现获取图像并对图像进行边缘检测(C++)

Baumer工业相机堡盟工业相机如何联合NEOAPI SDK和OpenCV实现获取图像并对图像进行边缘检测&#xff08;C&#xff09; Baumer工业相机Baumer工业相机使用OpenCV对图像进行边缘检测的技术背景在NEOAPI SDK里使用OpenCV建立边缘检测功能在NEOAPI SDK里使用边缘检测功能对图像进行…

网络基础面试题(二)

11.什么是网桥&#xff1f;防火墙的端口防护是指什么&#xff1f; 网桥是一种网络设备&#xff0c;用于连接两个或多个局域网&#xff08;LAN&#xff09;并转发数据包。它能够根据MAC地址来识别和转发数据&#xff0c;提高网络的传输效率和安全性。 防火墙的端口防护是指对防火…

prometheus 黑盒监控

黑盒监控 “白盒监控” 是需要把对应的Exporter程序安装到被监控的目标主机上&#xff0c;从而实现对主机各种资源以及状态的数据采集工作 ”黑盒监控“ 是不需要把Exporter程序部署到被监控的目标主机上&#xff0c;比如全球的网络质量的稳定性&#xff0c;通常用ping操作&am…

2019年认证杯SPSSPRO杯数学建模A题(第一阶段)好风凭借力,送我上青云全过程文档及程序

2019年认证杯SPSSPRO杯数学建模 纸飞机在飞行状态下的运动模型 A题 好风凭借力&#xff0c;送我上青云 原题再现&#xff1a; 纸飞机有许多种折法。世界上有若干具有一定影响力的纸飞机比赛&#xff0c;通常的参赛规定是使用一张特定规格的纸&#xff0c;例如 A4 大小的纸张…

数据结构——队列(Queue)

目录 1.队列的介绍 2.队列工程 2.1 队列的定义 2.1.1 数组实现队列 2.1.2 单链表实现队列 2.2 队列的函数接口 2.2.1 队列的初始化 2.2.2 队列的数据插入&#xff08;入队&#xff09; 2.2.3 队列的数据删除&#xff08;出队&#xff09; 2.2.4 取队头数据 2.2.5 取队…

让车辆做到“耳听八方”,毫米波雷达芯片与系统设计

摘要: 毫米波雷达,是指工作在毫米波波段(一般为30~300GHz频域,波长1~10mm)探测的雷达。毫米波雷达体积小、质量轻、空间分辨率高,穿透“雾烟灰”的能力强,还具备全天候全天时工作的优势。在智能网联汽车体系中,毫米波雷达是系统感知层不可或缺的重要硬件,能让智能驾…

python匹配问题

脏数据匹配 一般数据建模步骤中&#xff0c;数据清洗耗时占比80%以上&#xff0c;因为现实中接触到的数据相当脏&#xff0c;无法直接简单的用pandas的merge函数解决。下面以QS大学排名的匹配为例&#xff0c;简单介绍脏数据匹配中会遇到的问题和主要步骤。 1 问题描述 给定…

Django全文搜索

Django ORM允许你执行简单的匹配操作&#xff0c;例如contains过滤器&#xff08;或者不区分大小写的icontains&#xff09;。 比如查询正文中包含django的文章 from blog.models import Post Post.objects.filter(body__containsframework) 但是如果希望执行复杂的搜索查询&…

Vue.js设计与实现阅读2

Vue.js设计与实现阅读-2 1、前言2、框架设计的核心要素2、1 提升用户体验2、2 控制代码体积2、3 Tree-Shaking2、4 特性开关2、5 错误处理 1、前言 上一篇我们了解到了 命令式和声明式的区别&#xff0c;前者关注过程&#xff0c;后者关注结果了解了虚拟dom存在的意义&#x…

WPF常用技巧-多线程处理

WPF支持单线程单元模型&#xff0c;该模型与在Windows窗体应用程序中使用的模型非常类似&#xff0c;具有以下几条原则&#xff1a; WPF元素具有线程关联性。创建WPF元素的线程拥有所创建的元素&#xff0c;其他线程不能直接与这些WPF元素进行交互。WPF对象都在类层次的某个位…

Java后端开发——SSM整合实验

文章目录 Java后端开发——SSM整合实验一、常用方式整合SSM框架二、纯注解方式整合SSM框架 Java后端开发——SSM整合实验 一、常用方式整合SSM框架 1.搭建数据库环境&#xff1a;MySQL数据库中创建一个名称为ssm的数据库&#xff0c;在该数据库中创建一个名称为tb_book的表 …