clickhouse、timescaledb、parquet 在1min bar上压缩比例对比

我的市场数据存在 clickhouse 里, 不过 clickhouse 的连接驱动总是很麻烦, 而且 ORM 基本不支持 ch, 导致我行情数据存在 ch 里, 其他数据放到 sqlite/postgresql 里. 因而我一直想把行情数据换为「经典」的选择, 最近看到了tukuaiai/tradecat宣传得很好, 我就看了实现, 用的是 postgresql+timescaledb, 一看就非常经典.

于是我就简单测试了一下我最关注的压缩性能, 毕竟能在一块SSD里保存全市场数据还是很重要的. 剧透, timescaledb 压缩后大小甚至能到 clickhouse 的三倍.

我的 clickhouse 表结构是:

CREATE TABLE default.FUTURES_um_klines_BTCUSDT
(
    `open_time` UInt64 CODEC(DoubleDelta),
    `close_time` UInt64 CODEC(DoubleDelta),
    `open` Float64 CODEC(Gorilla(8), ZSTD(1)),
    `high` Float64 CODEC(Gorilla(8), ZSTD(1)),
    `low` Float64 CODEC(Gorilla(8), ZSTD(1)),
    `close` Float64 CODEC(Gorilla(8), ZSTD(1)),
    `volume` Float64 CODEC(ZSTD(1)),
    `quote_volume` Float64 CODEC(ZSTD(1)),
    `count` UInt32 CODEC(Delta(4), ZSTD(1)),
    `taker_buy_volume` Float64 CODEC(ZSTD(1)),
    `taker_buy_quote_volume` Float64 CODEC(ZSTD(1))
)
ENGINE = ReplacingMergeTree
ORDER BY open_time
SETTINGS index_granularity = 4096;

2024/06/01-2025/12/31 的 BTCUSDT Futures(Binance) 数据合计 32MiB, 压缩前 66MiB, 将数据读出后, 用 polars 默认参数写出为 parquet 后, 文件 35MiB.

pg 下的创建表语句是:

CREATE TABLE market_data.futures_um_klines_btcusdt_no_ms (
  open_time bigint NOT NULL,
  close_time bigint NOT NULL,
  open double precision NOT NULL,
  high double precision NOT NULL,
  low double precision NOT NULL,
  close double precision NOT NULL,
  volume double precision NOT NULL,
  quote_volume double precision,
  count integer,
  taker_buy_volume double precision,
  taker_buy_quote_volume double precision
);
 
CREATE INDEX futures_um_klines_btcusdt_no_ms_dt_idx
  ON market_data.futures_um_klines_btcusdt_no_ms USING btree (dt DESC);
 
SELECT create_hypertable(
	'market_data.futures_um_klines_btcusdt',
	'open_time',
	chunk_time_interval => 86400000,
	if_not_exists => TRUE
);

然后通过数_timescaledb_catalog.compression_chunk_size的方式计算 pg 表的大小, 得到 chunk_index_size=4592 kiB, compressed_chunk_size=94 MiB

这个数据非常吓人, 因为未压缩的 csv 也就 114MiB, 我试着删去open_time/close_time 两个字段, 压缩前的大小变小了一些, 但压缩后还是 94MiB, 那么应该可以说明压缩机制正常发挥作用. 另外, 我也检查了一下pgdata目录, 已经 1003 MiB 了.

这样的话, postgresql+timescaledb 压缩后大小基本是 ch 的三倍大小, 膨胀比例是五倍, 暂时还没有必要切换过去.

不过, 如果你去问 LLM, ch 中用我上面的表结构怎么样, 他们基本都会说不行, parts 太多, 元数据代价太大种种, 但是根据我的测试, 代价实际上非常小, 有机会我再写文章聊聊吧.

做个总结:

储存方式最近一年半 BTCUSDT 数据占用空间/MiB
clickhouse(压缩前)66
clickhouse(压缩后)33
parquet35
csv114
csv.zst37
timescaledb94