第二章:单体架构时代——早期交易系统的那些事儿
聊交易系统架构,咱们得从最开始的“石器时代”说起。2008年那会儿,我刚入行没多久,参与了一个期货交易系统的项目。那时候哪有什么微服务、云原生,一个系统能跑起来、不出错,就算烧高香了。
今天我就带大家回顾一下那个“单体架构”称王的年代。说白了,就是一个进程搞定所有事。
2.1 早期交易系统的架构特点
那个年代的交易系统,长什么样?我画了张图,你一看就明白。
你看这张图,结构特别清晰。客户端连上来,应用服务器一把梭,后面挂个数据库。所有逻辑——行情处理、订单路由、风控校验、资金管理——全在一个进程里跑。
我记得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。
- 优化代码:把一些热点数据从数据库搬到内存里,用共享内存做缓存。
- 限流:在高峰期,对部分非核心功能做降级处理。
这些方法,说白了都是“治标不治本”。但那个年代,大家就是这么过来的。
现在回头看,单体架构虽然问题多,但它确实撑起了早期交易系统的一片天。没有它,就没有后来的分布式架构、微服务架构。嗯,每个时代都有每个时代的“最佳实践”。
好了,这一章就聊到这儿。下一章,我们聊聊“集群时代”——当一台服务器不够用的时候,我们是怎么做的。