3. 数据存储选型:CSV、Parquet、HDF5、ClickHouse在高频场景下的优劣对比

做高频做市,数据存储是个绕不开的坎儿。

我见过不少团队,策略写得挺漂亮,结果倒在了数据读写上。你想想看,每秒几千笔 tick 数据,一天下来就是几千万行。如果存储选错了,回测跑一次要等半天,实盘更是想都别想。

今天咱们就来掰扯掰扯,CSV、Parquet、HDF5、ClickHouse 这四种方案,在高频场景下到底谁行谁不行。我会结合我踩过的坑,给你讲清楚。

核心结论先放这:高频场景下,CSV 只适合临时调试,Parquet 是离线分析的首选,HDF5 适合中等规模的结构化数据,ClickHouse 则是实盘级高频查询的王者。

3.1 CSV:最熟悉的陌生人

CSV 太常见了,几乎每个做量化的都用过。但说实话,在高频场景下,它就是个坑。

优点:

  • 人类可读,用记事本就能打开
  • 生态兼容性最好,Pandas、Excel 都能直接读
  • 不需要额外安装任何数据库

缺点(高频场景下致命):

  • 没有 schema 约束,字段类型全靠猜。我遇到过 CSV 里时间戳被读成字符串,回测结果全错,排查了一下午
  • 压缩率极差。同样一份 tick 数据,CSV 占 10GB,Parquet 可能只要 1GB
  • 读取速度慢。CSV 是纯文本解析,每行都要做字符串分割和类型转换。1000 万行数据,CSV 读一遍要 30 秒,Parquet 只要 3 秒
  • 不支持列式裁剪。你只想读 price 这一列,CSV 也得把整行全读进来

避坑指南:我曾经把 3 个月的期货 tick 数据全存成 CSV,结果回测时光读数据就花了 40 分钟。后来换成 Parquet,同样的数据 4 分钟读完。嗯,从那以后我再也没用 CSV 存过正式数据。

3.2 Parquet:离线分析的利器

Parquet 是我个人最推荐的离线存储格式。它本质上是列式存储,专为分析型负载设计。

为什么 Parquet 适合高频数据?

  • 列式存储:你只需要读需要的列,IO 量大幅减少
  • 内置压缩:支持 Snappy、Zstd 等算法,压缩比通常在 5:1 到 10:1
  • 谓词下推:可以按时间范围过滤,只读取符合条件的行组
  • 与 Spark、Dask、Polars 等大数据框架无缝集成

举个例子,假设你有 1 亿条 tick 数据,字段包括 timestamp、symbol、bid、ask、last_price、volume。如果你只想分析 last_price,Parquet 只需要读取这一列的数据块,而 CSV 必须把全部 6 列都读进来。

我的经验:做特征工程时,我习惯把原始 tick 数据存成 Parquet,然后按日期分区。这样回测时只需要加载特定日期的数据,速度飞快。而且 Parquet 的 schema 是自描述的,换个人来接手也能看懂字段类型。

但 Parquet 也有短板:

  • 不适合频繁更新。Parquet 文件是不可变的,你要修改数据只能重写整个文件
  • 小文件太多时性能下降。每个 Parquet 文件都有元数据开销,如果每个文件只有几 MB,反而比 CSV 还慢
  • 实时写入不友好。Parquet 是批处理格式,不适合逐条写入

3.3 HDF5:科学计算的老将

HDF5 在科学计算领域用得很多,量化圈子里也有一批忠实用户。它本质上是一个分层数据容器,支持多维数组存储。

HDF5 的优势:

  • 支持多维数据。比如你可以把 (日期, 合约, 时间) 组织成一个三维数组
  • 支持 chunked storage,可以按块压缩和读取
  • 支持并行 IO,多进程可以同时读写同一个文件的不同部分
  • Python 生态支持好,h5py 和 PyTables 都很成熟

但高频场景下要注意:

  • 单文件模式容易损坏。HDF5 把所有数据塞进一个文件,一旦文件损坏,数据全丢。我有个朋友就遇到过,回测跑了三天,结果 HDF5 文件打不开了……
  • 并发写入有坑。虽然支持并行 IO,但多进程同时写入同一个 HDF5 文件时,锁机制会导致性能急剧下降
  • 不适合超大规模数据。当数据量超过几十 GB 时,HDF5 的元数据管理会变慢

我的建议:如果你做的是中低频策略,数据量在 10GB 以内,HDF5 是个不错的选择。但高频做市的数据量通常以 TB 计,这时候 HDF5 就有点力不从心了。

3.4 ClickHouse:实盘高频的王者

ClickHouse 是俄罗斯 Yandex 开源的列式数据库,专为 OLAP 场景设计。我接触它是在 2020 年,当时被它的查询速度震惊了——10 亿行数据,聚合查询只要几百毫秒。

ClickHouse 的核心优势:

  • 真正的列式存储,按列压缩,压缩比极高
  • 向量化执行引擎,充分利用 CPU 的 SIMD 指令集
  • 支持实时写入,每秒可以写入几十万行
  • 分布式架构,可以横向扩展
  • SQL 接口,学习成本低

高频场景下的典型用法:

  • 用 MergeTree 引擎按时间分区,每天一个分区
  • 设置 ORDER BY (symbol, timestamp),加速按合约和时间的查询
  • 使用物化视图预计算常用指标,比如每分钟的 VWAP、波动率
-- 创建高频 tick 表
CREATE TABLE ticks (
    symbol String,
    timestamp DateTime64(3),
    bid Float64,
    ask Float64,
    last_price Float64,
    volume UInt64
) ENGINE = MergeTree()
ORDER BY (symbol, timestamp)
PARTITION BY toYYYYMMDD(timestamp)
TTL timestamp + INTERVAL 90 DAY;

-- 查询某合约最近 1 分钟的 tick 数
SELECT count() FROM ticks
WHERE symbol = 'rb2401'
  AND timestamp >= now() - INTERVAL 1 MINUTE;

避坑指南:ClickHouse 不适合频繁的更新和删除操作。它的 UPDATE 和 DELETE 是异步的,性能很差。如果你需要频繁修改数据,建议用 Kafka + ClickHouse 的流式写入方案,只追加不修改。

ClickHouse 的缺点:

  • 需要部署和维护,不像 CSV 那样开箱即用
  • 内存消耗大,复杂查询容易 OOM
  • 单机性能有限,数据量超过 10TB 建议上集群
  • 不适合小数据量场景,几百万行数据用 ClickHouse 有点杀鸡用牛刀

3.5 四种方案对比总表

维度 CSV Parquet HDF5 ClickHouse
读取速度(1亿行) 慢(~300秒) 快(~30秒) 中等(~60秒) 极快(~5秒)
压缩比 差(1:1) 优秀(5:1~10:1) 良好(3:1~5:1) 优秀(5:1~8:1)
实时写入 支持(但慢) 不支持 有限支持 支持(高吞吐)
列式裁剪 不支持 支持 支持 支持
并发读写 良好 中等 优秀
部署复杂度
适用场景 临时调试 离线分析 中等规模 实盘高频

3.6 我的选型建议

说了这么多,到底怎么选?我给出一个实际可操作的方案:

  1. 原始数据存储:用 ClickHouse。实盘产生的 tick 数据直接写入 ClickHouse,按天分区,保留 90 天。查询快,写入也快。
  2. 离线特征工程:用 Parquet。从 ClickHouse 导出数据到 Parquet 文件,按日期和合约分区。然后用 Polars 或 Dask 做特征计算,速度快,内存友好。
  3. 临时分析:用 CSV。调试代码、快速查看数据分布时,CSV 最方便。但用完就删,别留着占地方。
  4. HDF5:我个人现在用得少了。除非是处理多维数组数据(比如期权隐含波动率曲面),否则不太推荐。

一句话总结:高频做市的数据链路,我建议走「ClickHouse 存原始数据 → Parquet 做特征工程 → 内存 DataFrame 跑策略」这条路线。CSV 只做临时查看,HDF5 留给特定场景。

数据存储选型这事,没有银弹。关键是根据你的数据量、查询模式、实时性要求来权衡。你想想看,如果每天只有几万条数据,CSV 完全够用。但如果你跟我一样,每天处理几亿条 tick,那 ClickHouse + Parquet 的组合会让你省心很多。

高频做市数据存储选型决策树 数据存储选型 需要实时查询? 离线分析? 数据量 > 100GB/天? 需要 SQL 查询? ✅ ClickHouse 数据量 < 50GB? 需要多维数组? ✅ Parquet 数据量 < 10GB 且临时用? ⚠️ CSV(临时调试) 📦 HDF5(多维数组) 决策路径:根据数据量、实时性、查询模式选择最合适的存储方案 推荐方案 备选方案 决策节点

最后说一句,别在存储选型上纠结太久。先用 Parquet 跑起来,等数据量上来了再迁移到 ClickHouse。我见过太多人一开始就想搞个大而全的架构,结果项目还没跑起来就夭折了。先跑通,再优化,这才是做高频的务实之道。

专注资料整理