高频交易里,1毫秒的延迟可能意味着47万的滑点损失。但比延迟更隐蔽的杀手,是你选错了数据库类型。

Bhaskar Das在Medium上发了一篇技术复盘,讲一个真实场景:某交易团队用文档型数据库存储分钟级行情,结果回测时查询速度比预期慢了两个数量级。问题不在代码,在数据模型与查询模式的错配。

这不是个案。2024年某头部私募的技术负责人跟我聊过,他们花了3个月才把核心系统从MongoDB迁到专用时序数据库,迁移成本占当年技术预算的18%。

文档型 vs 时序型:交易场景的分水岭

文档型数据库(如MongoDB)把数据存成JSON-like的灵活结构,适合订单详情、账户配置这类半结构化数据。一条订单记录可能包含20个字段,不同交易所的字段还不一样,文档型能直接吞进去,不用改表结构。

时序数据库(如InfluxDB、TimescaleDB)则专门为时间戳数据优化。行情tick、技术指标(RSI、ATR这些)、传感器流数据——这些按时间顺序疯狂涌入的数据,时序型能用列式存储+时间分区,把写入吞吐量拉满。

Das在文章里打了个比方:文档型像瑞士军刀,时序型像手术刀。交易场景里,你用军刀做手术,不是不能做,是血会流得比较多。

具体数字很能说明问题。某测试环境下,单节点InfluxDB处理100万条tick数据的写入延迟是12毫秒,同配置MongoDB要到340毫秒。查询30天分钟线时,时序型的聚合查询快17倍。

但反过来,如果你要查"某用户过去3个月所有止盈订单的详细字段",文档型一条查询搞定,时序型得跨多个分区做关联,性能直接崩盘。

索引策略:藏在查询计划里的利润

索引策略:藏在查询计划里的利润

选对了数据库类型,下一步是索引。Das提到一个细节:很多团队在建索引时照搬关系型数据库的经验,结果在NoSQL里踩坑。

文档型数据库的复合索引有前缀匹配规则。你建了{symbol: 1, timestamp: 1}的索引,查询时如果只带timestamp条件,索引会失效。高频策略里这种查询很常见——比如"查所有品种在14:30的盘口",索引失效意味着全表扫描。

时序数据库的索引逻辑完全不同。它们通常自动按时间分区,但对标签(tag)的索引需要手动设计。InfluxDB里,把高频变化的字段(如价格)设成tag会导致索引爆炸,写入性能断崖下跌。正确的做法是把symbol、exchange这类低基数字段设tag,价格放field。

某量化团队的技术日志显示,他们曾因把order_id设成tag,导致单日写入从50万条降到8万条,排查了两天才发现是索引设计问题。

查询模式:你的策略代码在偷偷烧钱

查询模式:你的策略代码在偷偷烧钱

交易系统的查询模式决定了数据库的选型权重。Das梳理了三种典型场景:

实时信号生成:需要亚毫秒级的最新值查询,时序型的内存缓存+预聚合是刚需。某CTA策略用Redis+InfluxDB组合,把信号延迟从23毫秒压到1.8毫秒。

历史回测:需要批量拉取长时间序列,列式存储的压缩率和顺序读取优势巨大。1年的分钟线数据,时序型存储占用通常是文档型的1/5到1/10。

合规审计:需要随机访问特定订单的完整生命周期,文档型的灵活Schema更适合。监管要求保留5年交易记录,文档型的查询稳定性比时序型高40%(某券商2023年压测数据)。

没有银弹。大多数成熟团队采用混合架构:时序库存行情,文档库存订单,中间用消息队列同步。但这也带来一致性问题——某次网络分区后,两个库的同一笔订单状态不一致,风控系统误触发平仓。

Das在文章结尾提了一个被忽略的细节:某开源时序数据库的默认配置,会在内存不足时丢弃最新写入的数据,而不是阻塞等待。这对交易系统是灾难性的——你以为是数据延迟,其实是数据丢失。

你的生产环境检查过这个配置吗?