08_每日百万交易的支付系统分析
1704字约6分钟
2024-08-10
支付系统的核心业务流程
我们先简单了解下支付系统业务核心流程:
1、用户在商城提交一个支付订单请求
2、商城将订单支付请求提交给支付系统
3、支付系统指引用户跳转付款页面
4、用户指定付款方式,发起支付请求
5、支付系统调用第三方支付渠道进行支付
6、第三方支付渠道返回支付结果
7、支付系统返回支付结果给用户
每日百万交易的支付系统压力在哪里
从上面的业务流程图中可以看到,最核心的环节就是用户发起支付请求的时候,这时会生成一个支付订单
支付订单需要记录比如发起人、支付的商品、支付的渠道等信息
如果每日百万交易,抛开其他压力(高并发、数据存储),站在 JVM
的角度看,就是每天会在 JVM
内存里频繁的创建和销毁上百万个支付订单,这里牵扯一个核心的问题:
支付系统需要部署多少台机器
每台机器需要多大的内存空间
每台机器上启动的
JVM
需要分配多大的堆内存空间给
JVM
多大的内存空间才能保证可以支撑这么多的订单在内存中创建,而不会导致内存不够直接奔溃
站在 JVM 角度分析每日百万交易的支付系统
解决系统最核心的一个参数,那就是 JVM堆内存
的大小
支付系统每秒需要处理多少笔订单
假设每天 100
万个支付订单,一般用户交易行为发生在每天的高峰期,比如中午或者晚上
假设每天高峰期大概是几个小时,100
万平均分配到几个小时里,大概每秒 100
笔订单
假设支付系统部署了 3
台机器,那么实际上每台机器每秒大概处理 30
笔订单
每个支付订单处理的耗时
用户发起一次支付请求,那么需要在 JVM
中创建一个支付订单对象,给对象填充数据,然后把支付订单数据写入数据库,可能还会有一些其他逻辑处理
假设一次支付请求的处理需要 1
秒钟的时间
此时我们脑子里应该有一个流动的模型,每台机器一秒钟接收 30
笔支付订单请求,在 JVM
的新生代里创建 30
个支付订单对象
接着 1
秒钟之后,这 30
个支付订单处理完毕,然后这些支付订单对象的引用就没有了,这些订单对象在 JVM
的新生代里就是没人引用的垃圾对象
接着再是下一秒来 30
个支付订单,重复这个步骤
每个支付订单需要的内存空间
可以根据支付订单类中的实例变量类型来计算
比如支付订单类如下所示,一个 Integer
类型的变量数据是 4
个字节,Long
类型的变量数据是 8
个字节,其他类型变量占据多少字节可以百度查一下
public class Order {
private Integer userId;
private Long orderTime;
private Integer orderId;
}
一般支付订单这种核心类,按 20
个实例变量来计算,一个对象大概几百字节的样子,可以算大一点,一个支付订单对象占据 500
字节的内存空间,不到 1kb
每秒发起的支付请求对内存的占用
之前假设的有 3
台机器,每台机器每秒处理 30
笔支付订单请求
那么 30
个支付订单,大概占据的内存空间:30 * 500 字节 = 15000 字节
,大概 15kb
,非常小
对完整的支付系统内存占用分析
前面的分析只是基于一个核心业务流程的一个支付订单来分析的,只是系统中的一小部分
真实的支付系统线上运行,每秒肯定也会创建大量其他对象,我们可以简单估算一下,将之前计算结果扩大 10
到 20
倍
那么每秒钟创建出来被栈内存变量引用的对象大致占据的内存空间在几百 KB
到 1MB
之间
然后下一秒继续来新的请求创建大概 1MB
的对象放在新生代里,接着下一秒循环往复
循环多次之后,新生代垃圾太多了,就会触发 Young GC
回收到这些垃圾对象,这是一个完整系统 JVM
层面大致的内存使用模型
基于分析如何设置 JVM堆内存
一般来说线上业务系统,常见的机器配置是 2核4G
,或者是 4核8G
如果用 2核4G
的机器来部署,还是比较紧凑的,因为机器本身也要用一些内存空间,最后你的 JVM
进程最多也就 2G
内存了
然后这 2G
内存还得分配给方法区、栈内存、堆内存这几块区域,那么堆内存可能最多也就 1G
多的内存空间
然后堆内存还分新生代和老年代,你的老年代需要放置系统的一些长期存活对象,也需要个几百 MB
的内存空间,那么新生代可能也就几百 MB
的内存了
前面完整的支付系统内存占用分析,我们可以看到每秒会占据 1MB
左右的内存空间
如果新生代就几百 MB
的内存空间,运行几百秒之后新生代空间就满了,就会触发 GC
有一点需要明确:频繁触发 GC
一定不是什么好事情,是会影响线上系统的稳定性的
如果考虑采用 4核8G
的机器来部署支付系统,那么 JVM
进程至少可以给 4G
以上内存,新生代在里面至少可以分配到 2G
内存空间
这样子可以做到新生每秒多 1MB
左右内存,但是需要半个小时到一个小时才会让新生代触发 Young GC
,这就大大降低了 GC
的频率
当然,我们可以考虑不只部署 3
台机器,可以横向扩展更多的机器,这样每台机器处理请求更少,对 JVM
的压力更小