在证券交易系统设计与开发一文中,我们简单讨论了交易系统的核心架构设计,并给出了一个基本的代码框架。
有小伙伴说代码给得太简单,自己实现起来毫无头绪,能不能详细地说说架构设计。
本文的目的就是详细讲讲交易系统的核心架构设计。
从交易订单的角度看,一个订单的处理主要经过定序、撮合和清算三大步骤:
│
▼
┌─────────┐
│Sequence │
└─────────┘
│
▼
┌─────────┐
│ Match │
└─────────┘
│
▼
┌─────────┐
│Clearing │
└─────────┘
交易系统因为涉及到金钱的计算,要求100%准确可靠,因此,存储只能选择支持事务的SQL数据库。所有NoSQL数据库,都不在考虑范围内。
如果把订单全部存入数据库,并且,每次撮合都基于数据库的订单进行排序,这种完全基于数据库操作,并通过数据库事务保证数据一致性的交易系统,显然性能有限,并且,对数据库的硬件配置有非常高的要求。实际上,这种交易系统的处理能力每秒在100左右,提升系统性能完全依靠数据库服务器的硬件升级,成本高昂。
基于数据库对订单进行撮合,性能很低的原因在于,交易系统订单簿的买卖盘实际上是两个有序表,按照价格优先、时间优先的原则进行排序。如果每次撮合,都依赖数据库的查询排序,显然性能永远上不去。
如果把订单从数据库转移到内存中,那么,在内存中维护两个有序表就非常快,因为每次撮合,系统只需要取有序表的前面若干条符合条件的订单,显然,撮合速度有数量级的提升。
使用内存撮合的订单处理速度每秒可高达100万,因此,撮合引擎不再是系统瓶颈。然而,撮合虽然移到了内存,但清算仍然是基于数据库事务,因此,整个系统的订单处理能力大约能提升到每秒1000单,比全部基于数据库处理的第一代设计提高10倍左右。
有些交易系统非常鸡贼地声称自己每秒撮合百万单,但实际上,因为清算系统拖了后腿,整个系统的处理能力仍然不会超过每秒1千的水平。这就好比高速修了100车道,但收费站只有几个口,最终决定系统整体处理能力上限的仍然是清算系统的处理速度。
既然基于数据库清算严重制约了系统的订单处理速度,如果我们把清算系统也放到内存里,让订单在内存中完成撮合、清算的全部交易流程,那么,整个系统的处理速度可提高到每秒10万订单!
全内存模型和前面两种设计模型有个很大的区别,就是并不需要多线程并发处理,而是依赖单线程无锁模型,让所有计算全部在内存中完成,反而可以轻松获得10万+的量级。
使用全内存模型的好处除了极高的处理速度外,另一个好处是硬件成本很低,原因是内存非常便宜,单机32G内存就可以轻松实现每秒10万的订单处理速度。注意这里的每秒10万订单是指用户下单到订单撮合、清算全部完毕的整个流程,而不是单独指某个模块的处理速度。
使用全内存模型的缺点是由于内存的易失性,因为不再通过数据库事务保证数据一致性,如果遭遇宕机、断电等系统故障,交易系统必须能可靠地恢复整个系统的状态,这对系统的可靠性提出了非常高的设计要求。
对交易系统来说,传统的多线程模型对提升系统处理能力作用很小,使用无锁的单线程全内存设计可以极大地提升系统处理能力,LMAX Disruptor和Redis的单线程模型都证明了全内存无锁计算的高效率。