3. 数据存储架构:时间序列数据库、列式存储、内存数据库的选型对比

做Tick级数据,最头疼的问题是什么?

不是算法不够快,而是数据存不下、查不动。

我见过太多团队,一上来就拍脑袋选MySQL。结果呢?一张表几亿行,查询直接卡死。嗯,这里要注意——Tick数据的存储选型,决定了你整个系统的天花板。

3.1 三大存储方案的底层逻辑

先说说我个人的理解。Tick数据有三个特点:高频写入、按时间查询、数据量大。这三点直接决定了传统关系型数据库不适合。

目前主流方案就三个方向:

  • 时间序列数据库(InfluxDB、ClickHouse)——专为时序数据优化
  • 列式存储(Parquet)——适合批量分析和归档
  • 内存数据库(Redis)——追求极致的读写速度

说白了,没有银弹。每个方案都有自己的适用场景。

3.2 InfluxDB:专为时序而生

InfluxDB是我最早接触的时序数据库。它的设计哲学很纯粹——一切为了时间序列

核心优势:

  • 自动分片:按时间自动创建shard,不用手动管理分区
  • 数据压缩:对浮点数、时间戳有专门的压缩算法,压缩比能达到10:1
  • 连续查询:自动降采样,比如把1秒的Tick数据聚合为1分钟K线

我在项目中遇到过一个问题:InfluxDB的写入性能确实强,但查询复杂聚合时,内存消耗很大。有一次我们做全量Tick回放,直接撑爆了16G内存。

避坑指南:InfluxDB的tag(标签)不要太多。我曾经把股票代码、交易所、品种类型都设为tag,结果tag基数爆炸,写入性能直线下降。建议tag控制在5个以内。

3.3 ClickHouse:OLAP领域的黑马

ClickHouse这两年特别火。说实话,它确实更适合做Tick数据的分析。

为什么?

  • 列式存储:查询时只读取需要的列,IO开销小
  • 向量化执行:利用CPU的SIMD指令,批量处理数据
  • 物化视图:预计算聚合结果,查询秒级响应

举个例子。我们之前做Tick级别的波动率计算,InfluxDB需要3秒,ClickHouse只要200毫秒。差距就在这里。

-- ClickHouse创建Tick表
CREATE TABLE tick_data (
    symbol String,
    timestamp DateTime64(3),
    price Float64,
    volume UInt64,
    bid Float64,
    ask Float64
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (symbol, timestamp);

-- 物化视图:预计算每分钟统计
CREATE MATERIALIZED VIEW tick_minute_mv
ENGINE = SummingMergeTree()
ORDER BY (symbol, minute)
AS SELECT
    symbol,
    toStartOfMinute(timestamp) AS minute,
    count() AS tick_count,
    avg(price) AS avg_price,
    max(price) AS high,
    min(price) AS low
FROM tick_data
GROUP BY symbol, minute;
个人建议:如果你需要做复杂的统计分析,比如计算VaR、回测策略,ClickHouse是首选。但要注意,它的单行写入性能不如InfluxDB,适合批量导入。

3.4 Parquet:归档与分析的利器

Parquet不是数据库,而是一种列式存储格式。我通常用它来做冷数据归档

为什么需要Parquet?

  • 极高的压缩比:Snappy压缩后,Tick数据能压缩到原始大小的1/5
  • 与Spark/Pandas无缝集成:直接读取,不用转换
  • 支持谓词下推:只读取需要的行和列

我记得有一次,客户要求保留5年的Tick数据。如果用InfluxDB,存储成本高得吓人。最后方案是:热数据存InfluxDB(最近30天),冷数据转Parquet存对象存储。成本直接降了80%。

# Python写入Parquet示例
import pandas as pd
import pyarrow.parquet as pq

# 假设tick_df是DataFrame
tick_df.to_parquet(
    'tick_2024_01.parquet',
    compression='snappy',
    row_group_size=100000  # 每10万行一个row group
)
注意:Parquet不适合高频写入。每次写入都会生成新文件,文件数量多了以后,元数据管理会变得很麻烦。我建议每天或每小时生成一个文件。

3.5 Redis:极速缓存层

Redis在Tick系统里扮演的角色很特殊——它不是用来存全量数据的,而是做缓存和实时计算

典型场景:

  • 最新Tick缓存:用Redis Hash存储每只股票的最新Tick
  • 实时聚合:用Sorted Set存储每分钟的OHLC
  • 布隆过滤器:快速判断某个Tick是否已经处理过
# Redis存储最新Tick
HSET tick:latest:600519 price 1850.50 volume 1200 bid 1849.80 ask 1851.00

# 实时聚合:每分钟OHLC
ZADD tick:ohlc:600519:20240101 1704067200 "{\"open\":1850,\"high\":1860,\"low\":1845,\"close\":1855}"

我踩过一个坑:Redis的持久化。如果开启AOF,写入性能会下降30%以上。对于Tick数据,我建议关闭持久化,只做纯内存缓存。数据丢了可以从数据库恢复。

3.6 选型对比表

维度 InfluxDB ClickHouse Parquet Redis
写入性能 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐
查询性能 ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
压缩比 ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
存储成本
适用场景 实时监控、告警 复杂分析、回测 冷数据归档 缓存、实时计算

3.7 我的推荐架构

做了这么多年Tick系统,我总结了一套分层存储架构

Tick数据分层存储架构 Redis 缓存层 最新Tick / 实时聚合 / 布隆过滤器 InfluxDB 热数据层 最近30天Tick数据 / 实时监控 / 告警 ClickHouse 温数据层 历史Tick数据 / 复杂分析 / 策略回测 Parquet 冷数据层(对象存储)

这套架构的核心思想:热数据快、温数据全、冷数据省

  • Redis扛住高频读写,毫秒级响应
  • InfluxDB处理实时监控和告警
  • ClickHouse做深度分析和回测
  • Parquet归档历史数据,降低存储成本

嗯,这套方案我在多个项目中验证过,效果不错。当然,具体选型还要看你的数据量和查询模式。如果你每天只有几百万条Tick,单机InfluxDB就够了。但如果是千万级甚至亿级,还是老老实实上分层架构吧。

一句话总结:没有最好的数据库,只有最适合你场景的架构。Tick数据存储的核心是「分层」——让每一层做它最擅长的事。

专注资料整理