第二章:单体架构时代——早期交易系统的那些事儿

聊交易系统架构,咱们得从最开始的“石器时代”说起。2008年那会儿,我刚入行没多久,参与了一个期货交易系统的项目。那时候哪有什么微服务、云原生,一个系统能跑起来、不出错,就算烧高香了。

今天我就带大家回顾一下那个“单体架构”称王的年代。说白了,就是一个进程搞定所有事。

2.1 早期交易系统的架构特点

那个年代的交易系统,长什么样?我画了张图,你一看就明白。

单体架构时代:交易系统典型结构(2008年) 客户端(CTP/金仕达) 行情终端 · 交易终端 单体应用服务器 行情处理 · 订单路由 风控校验 · 资金管理 全部在一个进程里 数据库 Oracle/SQL Server 内部模块:紧耦合,共享内存 行情模块 交易模块 风控模块 所有功能打包成一个部署单元 扩展只能靠垂直扩容(加CPU、加内存) 数据库是唯一的“状态存储”

你看这张图,结构特别清晰。客户端连上来,应用服务器一把梭,后面挂个数据库。所有逻辑——行情处理、订单路由、风控校验、资金管理——全在一个进程里跑。

我记得2008年那会儿,我们用的服务器还是4核8G的。放到现在,连个手机都不如。但当时觉得,嗯,够用了。

2.2 单体架构的优缺点

先说说优点吧。毕竟能流行这么多年,肯定有它的道理。

优点

  • 开发简单:一个项目,一个IDE,一个团队。代码都在一个仓库里,调试起来特别方便。我那时候最怕的就是“联调”,但单体架构下,联调基本不存在。
  • 部署容易:打一个包,扔到服务器上,重启一下,完事。不像现在,动不动就是容器编排、服务发现,光配置就能写一天。
  • 性能不错:模块之间通过函数调用,没有网络开销。对于交易系统这种对延迟敏感的场景,这点特别重要。
  • 事务好处理:数据库本地事务,ACID特性天然支持。资金扣减、订单状态更新,一个事务搞定,不会出现数据不一致。

我个人习惯:在单体架构下,我最喜欢的就是“本地事务”。你想想看,一个事务里,先扣钱、再下单、最后更新状态,要么全成功,要么全回滚。多省心。

缺点

但缺点也很明显。说白了,成也萧何败也萧何。

  • 耦合严重:行情模块改了,可能影响交易模块。交易模块出了问题,整个系统都得停。我记得有一次,风控模块的一个内存泄漏,直接把整个交易进程搞挂了,所有客户都连不上。
  • 扩展困难:只能垂直扩容。加CPU、加内存、换更好的硬件。但硬件总有上限。而且,你想想看,行情处理需要CPU密集,订单路由需要IO密集,这两种负载混在一起,谁也讨不了好。
  • 维护成本高:代码量大了以后,编译一次要半小时。改一行代码,整个项目都得重新部署。我那时候最怕周五下午改代码,万一出问题,周末就泡汤了。
  • 技术栈锁定:一旦选定了技术栈,就很难换。比如用了Oracle,想换MySQL?那得把整个系统的数据访问层重写一遍。

我曾经踩过的坑:有一次,为了优化行情处理速度,我在一个共享内存里加了个缓存。结果交易模块也读这个缓存,两边没协调好,数据全乱了。那天晚上,我和DBA一起从备份里恢复数据,搞到凌晨三点。从那以后,我深刻理解了“模块间通信必须规范化”这个道理。

2.3 典型技术栈

那个年代的技术栈,现在看起来可能有点“复古”,但当时可是标配。

层级 技术选型 说明
数据库 Oracle / SQL Server 关系型数据库,支持ACID事务。Oracle更贵,但性能更好。SQL Server便宜些,小团队用得多。
应用服务器 Windows Server + .NET / C++ 期货行业,Windows是主流。.NET开发快,C++性能好。我们当时用C++,因为对延迟要求高。
中间件 MSMQ / IBM MQ 消息队列,用于异步处理。比如行情推送、订单确认等。
通信协议 TCP/IP + 私有协议 客户端和服务器之间,用自定义的二进制协议。效率高,但调试麻烦。
行情源 CTP / 金仕达 交易所提供的API,直接连到期货交易所。

你可能会问,为什么不用Linux?嗯,当时期货公司的IT部门,大部分都是Windows出身。而且,交易所提供的API,很多也只支持Windows。所以,Windows Server + C++ 是那个年代的黄金组合。

2.4 2008年期货交易系统案例

好了,说个具体的案例吧。2008年,我参与了一个期货交易系统的开发。这个系统是为一家中型期货公司做的,支持商品期货和股指期货。

系统规模

  • 并发用户:约500个交易员
  • 日均订单量:约20万笔
  • 行情延迟:要求小于100毫秒
  • 数据库:Oracle 10g,跑在小型机上
  • 应用服务器:4台Windows Server 2003,每台4核8G

架构设计

其实就是我上面画的那张图。4台应用服务器前面挂了个负载均衡器(硬件F5),后面共享一个Oracle RAC集群。每台服务器上跑着完整的交易系统,包括行情处理、订单路由、风控、资金管理。

遇到的问题

  • 数据库瓶颈:订单量上来后,数据库成了瓶颈。尤其是每天收盘前的“集合竞价”阶段,大量订单涌入,数据库CPU直接飙到100%。我们试过加索引、优化SQL,但效果有限。
  • 内存泄漏:C++写的系统,内存管理全靠自己。有一个模块在处理异常行情时,会不断分配内存但不释放。运行一周后,内存占用从2G涨到6G,最后系统崩溃。
  • 部署困难:每次升级,都要停掉所有服务。然后4台服务器依次更新,再重启。整个过程至少需要2小时。而且,一旦出问题,回滚也很麻烦。

避坑指南:如果你现在还在维护单体架构的交易系统,我建议你重点关注两点:一是数据库的慢查询,二是内存的使用情况。这两个地方最容易出问题。我曾经因为一个慢查询,导致整个系统的响应时间从10毫秒涨到了500毫秒,被客户骂得狗血淋头。

当时的解决方案

说实话,当时也没什么好办法。我们只能:

  • 加硬件:把数据库从4核升级到8核,内存从8G加到16G。
  • 优化代码:把一些热点数据从数据库搬到内存里,用共享内存做缓存。
  • 限流:在高峰期,对部分非核心功能做降级处理。

这些方法,说白了都是“治标不治本”。但那个年代,大家就是这么过来的。

现在回头看,单体架构虽然问题多,但它确实撑起了早期交易系统的一片天。没有它,就没有后来的分布式架构、微服务架构。嗯,每个时代都有每个时代的“最佳实践”。

好了,这一章就聊到这儿。下一章,我们聊聊“集群时代”——当一台服务器不够用的时候,我们是怎么做的。


专注资料整理