














先给结论:性能优化,立足于业务层优化,收益最大。当一段代码的"保险"价值前提已经不存在时,它就不再是保护,而是纯粹的负债。我们把网络红包的记账从两阶段(冻结→解冻→扣减)改成直扣模式,RPC 请求量省掉一半,记账流水砍掉 ⅔。这不是炫技,是一次业务分析驱动的架构简化。
随着 j'j'l 系统交易量的增多,并发交易量也跟着增多(每秒高达 80 多笔),系统优化、尤其是性能优化,成了首要任务。
j'j'l 网络红包的交易涉及企业校验、风控&延迟风控、交易单&通道单落库、资金冻结记账、请求三方通道、结果落库、同步与异步查单等环节。
这个时候,必须自顶向下,从宏观角度分析业务——因为业务层的优化,往往收益最大。
优化收益往往和改动层级成反比:越往底层抠(SQL、缓存、线程池),收益越薄;越往业务层看,空间越大。
本文要讲的,不是调优某个 SQL,也不是给某个接口加缓存。而是质疑一个"约定俗成"的记账模式本身——当业务前提变了,模式该不该跟着变。
答案是:该变。我们改了,效果立竿见影。
先看现状。j'j'l 网络红包发放交易的记账,沿用的是我司各产品线虚拟账户的通用做法——两阶段记账:
| 阶段 | 动作 | 方法 |
|---|---|---|
| 交易发起 | 冻结账户余额 | EnterpriseAccountingService#freeze |
| 交易成功 | 解冻并扣减余额 | EnterpriseAccountingService#transDeduction |
| 交易失败 | 解冻还原余额 | EnterpriseAccountingService#unfreeze |
拆开看,一次成功的交易,实际产生了 3 笔记账流水:
这套模式背后有一个明确的业务前提:
交易要请求三方通道(微信红包 / 支付宝零钱),而通道返回结果是异步的、不确定的、甚至可能长时间不返回的。
在这种前提下,资金必须有一个"中间态":
于是引入"冻结"作为中间态:先把钱锁住(既没划走,也动不了),等通道结果明确了,再决定是"解冻+扣减"还是"解冻还原"。
这是一套为"结果不确定"设计的防御式记账,逻辑自洽,没有错。
针对两阶段记账,我们能做什么优化?大胆假设,小心求证。我们的做法是自顶向下,先问业务:
两阶段记账的前提——"交易结果不确定、可能长时间悬置"——在 j'j'l 网络红包这个场景下,还成立吗?
结论是:不成立了。 依据是两条硬数据:
1. 时效性快。
依赖的三方通道是微信红包、支付宝零钱接口,交易通常在 10 秒内完成。没有"挂起几小时等结果"的情况。
2. 成功率高。
交易成功率 98% 以上。也就是说,绝大多数交易走的是"成功"这条路。
把这两条放一起,两阶段记账的价值链条就断了:
于是问题浮出水面:为了那不到 2% 的失败场景,我们让 98% 的交易都多付了 2 笔本不该有的记账开销(一次 freeze + 一次解冻)。
这不是优化不够狠的问题,是模式本身开始与业务失配了。
既然结果几乎总是"成功",那就反过来设计:
默认当成会成功,直接扣减;真失败了,再补回来。
| 阶段 | 动作 | 方法 |
|---|---|---|
| 交易发起 | 直接扣减 | EnterpriseAccountingService#deduct |
| 交易成功 | 无需记账 | —— |
| 交易失败 | 解冻还原余额 | EnterpriseAccountingService#deductRollback |
对比一下就清楚了:
失败的交易,回滚逻辑和原来一致(deductRollback),没有引入新的复杂度和风险。
这就是"直扣模式"——用"事后补偿"替代"事前防御"。
上线后,系统压力尤其是记账服务的压力,明显缓解。具体到数字:
| 指标 | 旧模式 | 新模式 | 降幅 |
|---|---|---|---|
| RPC 接口请求量 | 基准 | —— | 省掉约 ½ |
| 成功交易的记账流水 | 3 笔 | 1 笔 | 减少约 ⅔ |
在每秒 80+ 笔的并发量下,这笔账是很可观的——记账服务往往是资金链路上的瓶颈,流水每少一笔,都是一次实打实的吞吐释放。
回头看,这次优化真正的价值不在那几行代码,而在于三件事:
自顶向下,业务先行。 先从宏观业务找空间,而不是一头扎进技术细节。业务层的一个模式调整,收益远超底层几十处微调。
质疑"约定俗成"。 两阶段记账是行业通用做法,但不代表在每个场景都最优。当"结果不确定"这个前提不成立时,防御式设计就成了纯开销。
用数据支撑决策。 "10 秒内完成"和"98% 成功率"这两条,是把"我觉得可以改"变成"必须改"的关键。没有数据,这只叫赌;有了数据,才叫重构。
一句话收尾:性能优化,先看业务对不对,再看代码快不快。业务模式没对齐,代码再快也是给错误的设计打补丁。
事情是这样的。
我们有个系统叫 j'j'l,做网络红包发放的。随着交易量增多,并发也跟着涨,每秒高达 80 多笔 ————系统优化,尤其是性能优化,成了首要任务。
这笔交易的链路可不短:企业校验、风控和延迟风控、交易单和通道单落库、资金冻结记账、请求三方通道、结果落库、同步与异步查单…… 一串环节环环相扣。
这种时候,必须自顶向下,先从宏观角度分析业务。为什么?因为业务层的优化,往往收益最大。如果一上来就往下抠 SQL 慢查、加缓存、调线程池——那些有用,但收益薄。
顺着这个思路,我盯上了"记账"这块。
先交代背景。我们发红包,需要给商户虚拟账户记账,钱不是直接扣的,中间有个"冻结"环节:
这套逻辑,我司各产品线的虚拟账户都在用,是"约定俗成"的标准做法。
但注意:一笔成功的交易,背后其实记了 3 笔账——冻结、解冻、扣减。
这里先明确一个概念,免得后面看岔:文中的"扣减",指的是系统内虚拟账户的余额的减少(本地记账动作)。
为什么搞这么复杂?因为它有个前提:
三方通道的结果是异步的、不确定的,可能半天不回来。
所以钱不能直接扣,得先"锁"住,等结果明确了再决定扣还是还。
这逻辑没毛病,是给"结果不确定"做的防御式设计。
但我想问一句:这个前提,在 j'j'l系统 还成立吗?
j'j'l 交易存在两个显著的特征:
这两条一摆出来,问题就暴露了:
为了那不到 2% 的失败场景,我们让 98% 的交易,都多付了 2 笔本不该有的记账开销。
这不叫优化不够狠,这叫模式本身开始和业务失配了。
既然结果几乎总是成功,那就别防御了,直接:
对比一下就很直观:
失败的回滚逻辑没动,没引入新复杂度。就是把"事前防御"换成了"事后补偿"。
每秒 80 多的并发下,记账服务本来就是资金链路的瓶颈。每少一笔流水,都是一次实打实的吞吐释放。上线后,压力明显缓解。
这次改动代码量不大,但我觉得真正的价值在三件事:
一句话收尾:
性能优化,先看业务对不对,再看代码快不快。业务模式没对齐,代码写得再快,也是给错误的设计打补丁。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。