技术博客

清算性能优化:从 4 小时到 40 分钟的三个战场

第 56 / 141 篇 · 2025-04-25 · secfin · 清算性能优化数据库

清算批次的耗时从 4 小时压到 40 分钟,这类优化的共同点是:没有银弹,只有对瓶颈的连续正确打击。三个战场——IO、并行度、SQL——每个战场吃掉当前最大的一块,顺序错了就白费力气。这篇讲这个优化的方法论,每个战场各给一个具体的账。

测量先行:Amdahl 定律给出的排序

先算账再动手。Amdahl 定律说,把一个占总耗时比例为 $p$ 的环节优化到原来的 $\frac{1}{k}$,整体加速比是:

$$ S = \frac{1}{(1-p) + \frac{p}{k}} $$

代入真实场景感受这个公式的残酷:假设清算 4 小时里费用计算占 2 小时($p = 50\%$),你把它优化 10 倍($k=10$),整体只从 4 小时变成 $4 \times \frac{1}{0.5 + 0.05} \approx 2.2$ 小时。反过来,如果一个占总耗时 90% 的大块只能优化 2 倍,整体也能提速 1.8 倍。优化的价值由占比决定,不由技术炫酷程度决定——这就是为什么第一步永远是在每个任务边界打时间戳,画出真实耗时分布,而不是开会讨论"哪里可能慢"。实测里最常见的结果是耗时分布严重不均:少数几个任务吃掉大头,其中又以文件 IO 和全表扫描类 SQL 居多。

战场一:IO——批量系统的老冤家

清算系统的 IO 特征很典型:大批量小文件的顺序读(交易所和结算公司的下发文件)、批量流水生成后的随机写、以及每一步的落库 commit。三个具体手段:

  • 加载阶段的顺序化:下发文件按时间顺序读、解析批量入库,别逐行 commit。把 1 万行流水的逐行插入改成一次批量插入,实测能把加载阶段从 40 分钟压到 4 分钟——瓶颈从磁盘随机写变成顺序写。
  • 写路径合并:流水生成后先攒内存队列,按表批量 flush。注意崩溃恢复的设计:攒批的窗口期内的数据要有重做日志兜底,否则提速的代价是容错性。
  • 中间结果不落盘:有些实现把每个任务的中间结果物化成临时表供下游读,在数据量小的时候这是可读性设计,在千万级流水时这是纯开销。中间结果走内存传递、只在任务边界落盘,代价是任务间耦合变紧,调度系统要保证上游成功下游才启动——这本来就是依赖图该做的事。

战场二:并行度——分片与栅栏

并行化的前提是任务可切分。清算批次的天然切分维度是账户:费用计算、头寸更新、利息积数,绝大多数任务以账户为单位互不依赖。按账户号哈希取模分片:

$$ \text{分片号} = \text{hash}(\text{账户号}) \bmod N $$

$N$ 取 worker 数的倍数,保证负载均匀;哈希选账户号而不用客户号,是因为账户是账务处理的最小单位,同一账户的流水必须落在同一分片——这是正确性约束,不是负载均衡偏好。同一账户的两笔流水被分到两个 worker 并行处理,余额更新的先后就是未定义行为,不加锁就是丢更新。

但并行度受栅栏限制:分片内部并行之后,整个批次还有跨账户的汇总环节(资金汇总、对账 totals)必须等全部分片完成。用 Amdahl 的语言,可并行的占比决定天花板——如果汇总环节占总耗时 30%,分片再细也只能逼近 $1/0.3 \approx 3.3$ 倍。所以并行化要和汇总环节的优化配套:汇总环节的输入从"全量流水重扫"改成"各分片先算局部合计、主任务只合并 $N$ 个合计",汇总本身从 $O(M)$ 变 $O(N)$,天花板才真正打开。这也是《热点账户与并行清算》里讲过的"局部合计再合并"模式在日终批次的版本。

战场三:SQL——消灭 N+1 和隐式全扫

清算代码的 SQL 问题高度模式化,三个典型:

  • 循环内逐笔 UPDATE:费用计算的朴素实现是对每笔流水执行一次余额 UPDATE,10 万笔流水就是 10 万次行锁争抢加 10 万次日志写。改成单条 UPDATE ... CASE WHEN 或 MERGE,一次语句完成全量更新,实测差距是几十倍。
  • N+1 查询:解析文件时对每个代码去查一次证券信息表,1 万行文件就是 1 万次往返。预先把需要的证券信息一次性加载进内存哈希表,循环里只做查表。
  • 隐式全表扫描:流水表按交易日分区,但某条查询忘了带分区键条件,扫描退化到全历史。分区表上所有查询强制带分区键是写进开发规范的,代码评审专门盯这一项。

三个战场打完之后回头看总账:IO 手段砍掉第一个小时,并行化把可并行部分压掉,SQL 优化把单任务内部的低效清掉,4 小时到 40 分钟不是任何单一手段的功劳。反过来讲,任何一个战场单独做透也到不了这个数——这就是为什么优化计划要按测量结果动态排序,而不是按方案汇报的漂亮程度。

分片并行模型:同账户同分片,汇总局部化分片 1账户哈希 ≡0分片 2账户哈希 ≡1分片 N账户哈希 ≡N-1汇总任务合并 N 个合计栅栏:全部分片完成,汇总才能开始并行天花板 = 1 / 串行占比正确性约束:同一账户的全部流水落同一分片,否则余额更新顺序未定义SQL 三宗罪:循环内逐笔 UPDATE、N+1 查询、忘带分区键的全表扫描Amdahl:占 50% 的环节提速 10 倍,整体只快 1.8 倍——先测分布再动手