3. 数据存储选型:时序数据库与存储引擎深度对比

好,咱们进入第三个大块头——数据存储选型。

做回测系统,数据是命根子。你策略再牛,数据喂不对,全白搭。我见过太多团队,策略逻辑写得漂漂亮亮,结果一跑回测,数据加载慢得像蜗牛,或者磁盘空间几天就爆了。说白了,存储选型没做好,后面全是坑。

这一章,我重点聊三个事:InfluxDB vs ClickHouse 怎么选、列式存储与压缩算法为什么是回测的救星、以及数据分区与生命周期管理怎么落地。嗯,都是实战里必须啃的硬骨头。

3.1 时序数据库对决:InfluxDB vs ClickHouse

先问个问题:回测数据有什么特点?

  • 写多读少:数据一旦写入,几乎不改动。
  • 按时间范围查询:比如“查2023年1月到3月的所有tick数据”。
  • 数据量巨大:高频数据一天就能产生几亿条。

所以,传统的关系型数据库(MySQL、PostgreSQL)在这里基本歇菜。我们得用专门为时序数据设计的引擎。

我个人最常用的两个选项:InfluxDBClickHouse。它们俩路子不太一样。

对比维度 InfluxDB ClickHouse
定位 纯时序数据库 列式分析数据库(时序是强项之一)
写入性能 极高,单机每秒百万级 极高,批量写入更优
查询灵活性 强在时序聚合(降采样、窗口函数) 强在复杂分析(JOIN、子查询、多维度聚合)
存储压缩比 较好(自带压缩) 极好(列式存储 + 多种压缩算法)
运维复杂度 简单,开箱即用 中等,需要调优配置
适用场景 纯时序监控、IoT、简单回测 大规模回测、实时数仓、复杂分析

我的选择建议:

  • 如果你团队小、数据量在TB级以内、主要做日频或分钟级回测,InfluxDB 上手更快。
  • 如果你做高频、数据量在PB级、需要做复杂的因子计算或跨品种分析,ClickHouse 是更优解。

我记得有一次帮一个私募搭回测系统,他们一开始用的InfluxDB,数据量到500亿条时,查询降采样开始变慢。后来迁移到ClickHouse,同样的查询快了近10倍。嗯,选型这事,得提前想清楚未来三年的数据规模。

3.2 列式存储与压缩算法:为什么能省90%空间?

你想想看,回测数据里,每一行都有时间戳、品种代码、开盘价、收盘价、最高价、最低价、成交量……但大多数查询只关心某几个字段,比如“我只想查某只股票过去一年的收盘价”。

如果是行式存储(比如MySQL),它会把一整行数据都读出来,哪怕你只想要一个字段。这就像你去图书馆借书,明明只想看第100页,管理员却把整本书搬给你。浪费啊。

列式存储就不一样了。它把每一列单独存放。你查收盘价,它只读收盘价那一列的数据。磁盘IO瞬间降下来。

而且,列式存储天然适合压缩。为什么?因为同一列的数据类型相同,数值往往很接近。比如收盘价,大部分在10到100之间波动。用压缩算法一压,效果惊人。

我常用的压缩算法有这些:

  • LZ4:压缩速度快,解压也快。适合对查询延迟敏感的场景。
  • ZSTD:压缩比更高,但压缩/解压稍慢。适合冷数据归档。
  • Delta Encoding:存储相邻值的差值。比如价格序列,差值往往很小,用更少的bit就能存。
  • Run-Length Encoding (RLE):适合重复值多的列,比如“交易所代码”这种枚举值。

实战经验:

我在ClickHouse里存过1TB的原始tick数据,用ZSTD压缩后,只用了120GB。压缩比接近9:1。你想想看,省了多少存储成本。

下面是一个简单的ClickHouse建表示例,展示了列式存储和压缩算法的配置:

CREATE TABLE tick_data
(
    symbol      String,
    timestamp   DateTime,
    price       Float64,
    volume      UInt64,
    exchange    String
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (symbol, timestamp)
SETTINGS index_granularity = 8192;

注意看,ORDER BY 决定了数据的物理排序。我习惯把 symbol 放前面,因为查询时经常按品种过滤。这样能极大提升查询效率。

3.3 数据分区与生命周期管理

数据量一大,不分区就是灾难。你想想,几万亿条数据放在一个表里,查询时全表扫描,神仙都救不了。

分区就是把数据按某个规则切成小块。最常见的分区键是时间。比如按天、按月、按年分区。

为什么按时间分区?因为回测查询几乎总是带时间范围的。比如“查2023年1月的数据”,如果按月分区,系统只需要扫描1月份那个分区,其他11个月的数据碰都不碰。

我建议的分区策略:

  • 高频数据(tick、秒级):按天分区。一天一个分区,查询粒度细,清理也方便。
  • 中频数据(分钟级):按月分区。数据量适中,分区数不会太多。
  • 低频数据(日频):按年分区。甚至可以不分区,直接全表扫描也很快。

注意:分区不是越多越好。分区数太多,元数据管理开销会变大。比如ClickHouse,一个表超过几千个分区,插入和合并性能会下降。我一般控制单表分区数在1000以内。

说完分区,再说生命周期管理。说白了,就是数据什么时候该删。

回测数据有个特点:越老的数据,被查询的频率越低。比如2020年的数据,可能一年才被翻出来用一次。但2023年的数据,几乎天天被查。

所以,我们可以给数据设一个“保鲜期”。比如:

  • 热数据(最近3个月):存在SSD上,查询最快。
  • 温数据(3个月到2年):存在普通HDD上,查询稍慢但能接受。
  • 冷数据(2年以上):压缩归档,甚至删掉原始数据,只保留降采样后的聚合数据。

在ClickHouse里,可以用 TTL 自动管理生命周期:

ALTER TABLE tick_data
    MODIFY TTL timestamp + INTERVAL 2 YEAR TO DISK 'hdd',
    MODIFY TTL timestamp + INTERVAL 5 YEAR DELETE;

这段代码的意思是:超过2年的数据自动迁移到HDD磁盘,超过5年的数据自动删除。完全自动化,省心。

避坑指南:

我曾经犯过一个错:把TTL设得太激进,结果某次回测需要查3年前的数据,发现已经被自动删了。后来我改成“先归档再删除”的策略——先压缩成Parquet文件存到对象存储(比如S3),确认不需要了再删。嗯,数据安全第一。

3.4 知识体系总览

下面这张图,是我对本章核心逻辑的总结。你可以把它当作选型和设计的参考地图。

数据存储选型核心逻辑 回测数据特点 写多读少 · 时间范围查询 · 数据量巨大 InfluxDB 纯时序 · 简单运维 · TB级以内 ClickHouse 列式分析 · 复杂查询 · PB级 列式存储与压缩 LZ4 · ZSTD · Delta Encoding · RLE 压缩比可达 9:1,节省90%空间 分区与生命周期管理 按天/月/年分区 · TTL自动清理 热SSD → 温HDD → 冷归档 高效、低成本、可扩展的回测数据底座 查询快 · 存得省 · 管得轻松

这张图从数据特点出发,引出两个主流选型,再落到列式存储和分区管理两个核心技术点上。最终目标就一个:让你的回测系统跑得快、存得省、管得轻松。

好了,数据存储选型这块,我就聊这么多。下一章咱们会深入数据清洗与预处理,那是另一个容易踩坑的地方。到时候再细说。


蓝海数据掘金营,专注资料整理