system-design-notes:Sequencer排序器与确定性执行,图解交易所的"核心心脏"
【免费下载链接】system-design-notesNotes of the book System Desgin Interview - An Insider's Guide项目地址: https://gitcode.com/GitHub_Trending/sy/system-design-notes
system-design-notes 是一套系统设计的面试笔记项目,本文带你深入其中第 28 章「Stock Exchange(交易所)」,拆解Sequencer 排序器与确定性执行——交易所系统的核心心脏:它如何为每秒数万笔订单公平"排号",用单写者 + mmap 事件存储把延迟压到微秒级,并在故障后快速恢复。
💡本文要点预览
- Sequencer 为每笔订单/成交打上全局唯一序列号,公平性可验证
- 单写者 + 环形缓冲 + mmap 事件存储 = 微秒级低延迟
- 确定性执行:顺序由序列号决定,而不是墙钟时间
- 领导者选举 + 热备副本 → 99.99% 可用性
为什么"公平"是交易所系统设计的核心难题
先看量级:一天约 10 亿笔订单,交易时段 6.5 小时,平均 QPS 约 4.3 万,开市高峰可达21.5 万 QPS。当数万用户同时抢购同一本订单簿时,"谁先谁后"直接决定谁能以更优价格成交——顺序一旦模糊,就会引发争议甚至监管风险。Sequencer 排序器要解决的,正是这个"排号"问题。

在交易主链路(Critical Path)中,订单的旅程是:券商 → 客户端网关 → 订单经理 →Sequencer→ 撮合引擎。市场数据流与报表流不在关键路径上,延迟要求相对宽松,但交易流必须极致优化。
Sequencer排序器:交易所的"心脏"长什么样?

Sequencer 是让撮合引擎确定性运行的关键组件,它分为两半:
- Inbound Sequencer(入站):为进入撮合引擎的每笔订单盖上序列号
- Outbound Sequencer(出站):为撮合产生的每笔成交(fill)盖上序列号
为什么必须打号?三个理由:
- ⚖️及时性与公平性——"先到先得"有了可验证的客观依据
- 🔁快速恢复与重放——故障后按序列号重放事件流即可重建状态
- ✅恰好一次(exactly-once)保证——凭序列号可识别并去重重复事件
一个有意思的取舍:Kafka 本质上就是"入站 + 出站"的消息队列,概念上完全可以当 Sequencer 用;但为了追求更低延迟,笔记中选择自己实现,这正是"心脏"必须长在体内、不能外包的原因。
深入剖析:单写者 + 环形缓冲 + mmap 事件存储

事件溯源(Event Sourcing)视角下,Sequencer 的定位发生了微妙的变化——它不再自己充当事件存储,而是单写者(single writer):先排序,再转发。整个流程分三步:
- 网关(Gateway)与撮合引擎各自把事件写进环形缓冲(ring buffer)——预分配空间、无锁,避免内存分配开销
- Sequencer 从环形缓冲中拉取数据,统一盖上序列号
- 写入Event Store(基于 mmap 的共享内存总线)
各组件只需订阅事件存储,就能拿到自己需要处理的事件;订单经理甚至以库的形式在每个组件中持有一份副本,省掉一次跨进程调用。
低延迟实践:单服务器 + mmap 总线
现代交易所有一个反直觉的做法:不像大多数软件那样分布式部署,而是把所有核心组件跑在一台巨型服务器上。

如果按最初的分布式设计,瓶颈在于服务间网络延迟和 Sequencer 的磁盘读写,端到端只能做到几十毫秒;而目标是几十微秒。把关键路径搬上同一台机器、进程间通过 mmap 总线通信后,再配上一个小技巧——把 mmap 文件建在/dev/shm(共享内存)里,就彻底告别了磁盘访问。
应用循环:告别上下文切换与锁竞争

另一个关键优化是应用循环(Application Loop):用一个 while 循环持续执行使命关键任务,并把进程绑定到固定 CPU 上,避免操作系统线程调度的上下文切换。它的附带好处是天然没有锁竞争——单线程单循环,多线程抢同一资源的场景直接消失了。
确定性执行如何成立:序列号决定顺序

这是 Sequencer 最精妙的地方:事件实际发生的时刻不重要,重要的是它在序列中的位置。
上图中,6 个事件在时间轴上分布疏密不均;进入 Sequencer 之后,它们被重新排成严格有序的队列。撮合算法在处理时还会校验:若收到的序列号不是预期的nextSequence,直接返回OUT_OF_ORDER错误拒收。这就是功能确定性——同样的事件序列,在任何节点、任何时刻重放,都会得到完全一致的撮合结果。
笔记中同时提醒要关注延迟确定性:通过监控 99 / 99.99 分位延迟追踪毛刺,典型元凶是 Java 的垃圾回收(GC)停顿。
构建 99.99% 可用性:领导者选举与热备副本

99.99% 的可用性意味着每天最多停机 8.64 秒。围绕"单台机器跑核心"的架构,高可用方案分两层:
- 进程级:部署备用的撮合引擎实例待命,自动检测故障并快速切换。有状态组件在非领导状态下可以接收入站事件、但不发布出站事件,心跳探测主副本是否失能
- 服务器级:整台服务器作为热/温备机,事件存储通过可靠 UDP跨机复制,比 TCP 更快
即使热备也倒下怎么办?笔记给出了跨城市多数据中心复制的思路,并用 Raft 类领导者选举(term 任期机制)决定由谁接管。对交易所而言数据丢失不可接受,必须高频备份。
学习指南:Stock Exchange 全章与相关笔记
📚本章全文:28. Stock Exchange/README.md,按"理解问题 → 高层设计 → 深度剖析 → 总结"四步展开,覆盖撮合算法、行情多播、Colocation 托管等主题。
延伸阅读(同一项目内):
- 事件溯源完整细节:27. Digital Wallet/README.md
- 消息队列背景知识(Sequencer 与 Kafka 的取舍):19. Distributed Message Queue/README.md
- 全局唯一 ID 与序列号生成:07. Unique-Id Generator/Readme.md
- 一致性哈希在分片中的应用:05. Consistent Hashing/Readme.md
一句话总结:Sequencer 排序器用"全局序列号"这一个简单机制,同时买到了公平性、可重放性和故障恢复能力;再叠加单写者、mmap 总线与应用循环,就构成了交易所那颗稳定跳动的心脏。
【免费下载链接】system-design-notesNotes of the book System Desgin Interview - An Insider's Guide项目地址: https://gitcode.com/GitHub_Trending/sy/system-design-notes
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考