3. 数据存储选型:时序数据库与存储引擎深度对比
好,咱们进入第三个大块头——数据存储选型。
做回测系统,数据是命根子。你策略再牛,数据喂不对,全白搭。我见过太多团队,策略逻辑写得漂漂亮亮,结果一跑回测,数据加载慢得像蜗牛,或者磁盘空间几天就爆了。说白了,存储选型没做好,后面全是坑。
这一章,我重点聊三个事:InfluxDB vs ClickHouse 怎么选、列式存储与压缩算法为什么是回测的救星、以及数据分区与生命周期管理怎么落地。嗯,都是实战里必须啃的硬骨头。
3.1 时序数据库对决:InfluxDB vs ClickHouse
先问个问题:回测数据有什么特点?
- 写多读少:数据一旦写入,几乎不改动。
- 按时间范围查询:比如“查2023年1月到3月的所有tick数据”。
- 数据量巨大:高频数据一天就能产生几亿条。
所以,传统的关系型数据库(MySQL、PostgreSQL)在这里基本歇菜。我们得用专门为时序数据设计的引擎。
我个人最常用的两个选项:InfluxDB 和 ClickHouse。它们俩路子不太一样。
| 对比维度 | 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 知识体系总览
下面这张图,是我对本章核心逻辑的总结。你可以把它当作选型和设计的参考地图。
这张图从数据特点出发,引出两个主流选型,再落到列式存储和分区管理两个核心技术点上。最终目标就一个:让你的回测系统跑得快、存得省、管得轻松。
好了,数据存储选型这块,我就聊这么多。下一章咱们会深入数据清洗与预处理,那是另一个容易踩坑的地方。到时候再细说。