(十二)web3面试--撮合交易

vvEcho 2025-12-26 19:34:31
Categories: Tags:

1.撮合引擎怎么设计的?

基于内存计算的撮合引擎,要满足以下的几点:
低延迟、撮合的确定性、可恢复、要与风控平台解耦

整体链路是:API-风控-撮合引擎(内存)-成交落库-资产变更
撮合只做一件事:确定成交结果,不直接操作资产(在 cex中撮合引擎只计算保存撮合的结果,不直接操作资产,它只会产出成交事件,推送给资产系统,行情系统,推送系统)

(1.1)那么问题来了,资产什么时候被操作

用户下单

风控系统冻结资产(这一步已经发生)

撮合引擎计算成交结果(不碰钱)

成交事件 Trade

资产系统消费 Trade

真正修改账户余额

(1.2)资产变化要做到可回放,可审计

可回放指:不依赖账户当前状态,只靠交易历史账单,即可把账户状态从 0 算出来,并且和现在结果一致

(1.3)流水表越来越大,重放一次要几个小时,怎么办?

做增量回放,而不是全量回放;定期回放给出一个确切标点,定期把这个回放结果冻结成一个可信状态
一般高频账户,每1000 条交易记录做一次或者每 5 分钟回放一次;低频用户一般为每日做一次

(1.4)成交事件发给资产系统失败了,怎么办? 撮合已经成功了,钱还没变,系统怎么保证最终一致?

只要资产没入账,交易在系统层面就是「未完成态」,必须通过可靠事件 + 重试 + 幂等 + 对账补偿 保证最终一致

撮合成功时,做的不是“转钱”,而是生成一笔不可变的成交事件

事件怎么保证不丢?

异步投递消息:失败重试,MQ挂了等恢复,不影响前面撮合总流水

兜底: 定时扫描交易表,查看已经撮合成功的单和资产流水变里,有没有对应的资产变化流水;有对应的流水,修改对应的资产变化状态,没有则重发;(重复时需要避免重复消费)

2.撮合引擎涉及到哪些表或数据结构

1
2
3
4
class OrderBook {
TreeMap<Price, OrderQueue> bids; // 买单,价格倒序
TreeMap<Price, OrderQueue> asks; // 卖单,价格正序
}

TreeMap:保证价格有序
OrderQueue:同价位 FIFO(时间优先)

1
2
3
4
5
6
7
8
9
10
11
12
-- 订单表
orders (
order_id,
user_id,
symbol,
side,
price, -- 订单委托价格(限价单)
amount,-- 订单的【原始委托总量】
filled_amount,--订单【累计已成交量】(不是“本次”)
status,
created_at
)

–快照表,挂单表,资产变更流水表,资产冻结表

3.撮合引擎的执行流程是怎样的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 新订单进来

检查最优对手价

while (可成交 && qty > 0)
- 从对手price queue 取 head
- 成交min(qty)
- 生成Trade
- 更新两边剩余量
- 如果对手单完了 → 出队
- 如果 price 队列空 → remove price

如果自己还有剩余
- 挂入自己的 price → queue
否则
- 订单结束,生命周期完结

4.撮合引擎怎么确保是单线程的?以及为啥不设计成多线程?如果设计成多线程会引发哪些问题?

撮合引擎怎么确保单线程?
撮合系统采用的是 Single Writer 架构。
网关层按对应币对儿的symbol做确定性路由,同一交易对在任一时刻只会被一个撮合执行流处理。
其他节点只做热备和状态订阅,不参与写入。
主备切换是通过事件日志replay完成的,不存在双写窗口,因此可以保证顺序一致性和可回放性

为何不设计成单线程?
1.单核单线程CPU 跑满的情况下,基于内存的撮合,10 万级别TPS可以做到毫秒级延迟
2.单线程没有锁,内存连续 cache命中率高;指令路径短
3.金融系统的设计是基于确定性和可回放性,对于撮合来说最终的是顺序一致结果唯一,通过“一个交易对一个线程”的模型,交易所把并发问题前移到路由层,用内存换无锁,用确定性换稳定性。

设计成多线程会面临的问题?
多线程撮合最大的问题不是性能,而是破坏了撮合结果的确定性。
在多线程环境下,很难严格保证价格优先、时间优先,成交顺序依赖线程调度,导致结果不可重放。
即使通过锁保证互斥,也无法保证顺序公平性,反而引入延迟抖动、死锁和一致性风险。
因此主流交易所采用single-writer架构,用单线程换确定性,用内存换稳定

5.交易所有哪些模块?

交易所 = 钱包 + 账户 + 交易 + 撮合 + 清算 + 行情 + 风控