数据库选型与配置:关系型 vs 时序数据库

做量化实盘,数据库选型是个绕不开的坎。我见过不少团队,一开始随便选个MySQL存行情,结果跑了一周,查询速度慢得像蜗牛。说白了,选错数据库,你的策略再牛也白搭。

今天咱们聊聊关系型数据库和时序数据库的区别,以及怎么搭配合适。我个人习惯把数据库选型分成两步:先看数据类型,再看查询模式。

关系型数据库:MySQL vs PostgreSQL

关系型数据库适合存什么?订单、持仓、用户信息、策略配置。这些数据的特点是:结构固定、需要事务支持、经常做增删改查。

MySQLPostgreSQL 我都用过。MySQL上手快,社区资源多,但遇到复杂查询就有点吃力。PostgreSQL功能更强,支持窗口函数、CTE、JSON字段,做回测分析时特别顺手。

举个例子,查某个策略最近30天的每日盈亏:

-- PostgreSQL 写法,简洁高效
SELECT 
    date_trunc('day', trade_time) as trade_date,
    SUM(pnl) as daily_pnl
FROM trades
WHERE strategy_id = 'strat_001'
  AND trade_time >= NOW() - INTERVAL '30 days'
GROUP BY 1
ORDER BY 1;

MySQL写同样的逻辑,得用DATE_FORMAT函数,性能还差一截。我个人建议:新项目优先选PostgreSQL,除非你团队里全是MySQL老手。

核心结论:关系型数据库存「状态」数据,时序数据库存「过程」数据。别混着用。

时序数据库:InfluxDB vs ClickHouse

行情数据、Tick数据、K线数据,这些是时序数据库的强项。为什么不用MySQL?你想想看,每秒几千条Tick写入,MySQL的B+树索引扛不住,写入延迟会越来越高。

InfluxDB 专为时序场景设计,写入快,查询语法简单。我早期做高频策略时就用它存Tick数据。但有个坑:InfluxDB的存储压缩率一般,数据量大了以后磁盘占用很吓人。

ClickHouse 是后来崛起的列式存储数据库。它的压缩率惊人,同样的数据量,磁盘占用只有InfluxDB的1/3到1/5。查询速度也快,特别是做聚合分析时。

我曾经在项目里用ClickHouse存全市场Level-2行情,每天数据量约50GB,查询1分钟K线只需要几十毫秒。InfluxDB做同样的查询,得等好几秒。

特性 InfluxDB ClickHouse
写入速度 极快
查询速度(聚合) 中等 极快
存储压缩率 一般
学习成本
适用场景 中小规模实时监控 大规模历史分析

我的建议:实时监控用InfluxDB,历史回测和数据分析用ClickHouse。两者可以共存,各司其职。

数据库连接池配置

连接池配置看似简单,但坑不少。我见过有人把连接池设成100,结果数据库直接挂了。为什么?因为每个连接都占用内存和文件句柄,太多连接反而拖垮性能。

以Python的 SQLAlchemy 为例,我常用的配置:

from sqlalchemy import create_engine

engine = create_engine(
    'postgresql://user:pass@host:5432/db',
    pool_size=10,           # 连接池大小
    max_overflow=5,         # 最大溢出连接数
    pool_timeout=30,        # 等待连接超时(秒)
    pool_recycle=3600,      # 连接回收时间(秒)
    pool_pre_ping=True      # 连接前检查是否可用
)

几个关键点:

  • pool_size 别设太大,10-20就够了。策略程序一般就几个线程,用不了那么多连接。
  • pool_recycle 一定要设。数据库默认8小时断开空闲连接,不回收的话,第二天你会发现一堆死连接。
  • pool_pre_ping 建议开启。每次从池里拿连接前先ping一下,避免拿到坏连接。

避坑指南:我曾经把pool_size设成50,结果数据库CPU飙升到100%。后来发现是连接太多,每个连接都在做空闲检查。改成10之后,一切正常。

数据表结构设计

表结构设计直接影响查询性能。我总结了几条经验:

1. 订单表

CREATE TABLE orders (
    order_id       VARCHAR(32) PRIMARY KEY,
    strategy_id    VARCHAR(16) NOT NULL,
    symbol         VARCHAR(10) NOT NULL,
    side           CHAR(1) NOT NULL,  -- 'B' 买, 'S' 卖
    order_type     VARCHAR(8) NOT NULL, -- 'LIMIT', 'MARKET'
    price          DECIMAL(16,4),
    quantity       DECIMAL(16,4),
    filled_qty     DECIMAL(16,4) DEFAULT 0,
    status         VARCHAR(12) NOT NULL,
    created_at     TIMESTAMP NOT NULL,
    updated_at     TIMESTAMP NOT NULL
);

-- 常用查询索引
CREATE INDEX idx_orders_strategy ON orders(strategy_id, created_at);
CREATE INDEX idx_orders_symbol ON orders(symbol, created_at);

2. Tick行情表(时序)

-- ClickHouse 建表示例
CREATE TABLE tick_data (
    symbol      String,
    timestamp   DateTime64(3),
    price       Float64,
    volume      Float64,
    bid_price   Float64,
    ask_price   Float64
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (symbol, timestamp);

注意这里用了 PARTITION BY 按月分区。为什么?因为Tick数据量太大,不分区的话,查询一个月的数据要扫描全表。分区后,查询只扫对应分区,速度快很多。

3. 策略运行日志表

CREATE TABLE strategy_logs (
    log_id       BIGSERIAL PRIMARY KEY,
    strategy_id  VARCHAR(16) NOT NULL,
    log_level    VARCHAR(8) NOT NULL,  -- 'INFO', 'WARN', 'ERROR'
    message      TEXT NOT NULL,
    created_at   TIMESTAMP NOT NULL DEFAULT NOW()
);

-- 按时间分区,方便清理旧日志
CREATE INDEX idx_logs_strategy_time ON strategy_logs(strategy_id, created_at);

小技巧:日志表建议定期清理。我一般保留30天数据,用定时任务删除旧记录。不然一张表存几年日志,查询会越来越慢。

知识体系结构图

下面这张图概括了数据库选型的核心逻辑:

量化策略数据库选型决策树 数据类型是什么? 状态数据 订单、持仓、配置 时序数据 行情、Tick、K线 MySQL PostgreSQL(推荐) InfluxDB(实时) ClickHouse(分析) 核心原则 1. 状态数据 → 关系型数据库(PostgreSQL优先) 2. 过程数据 → 时序数据库(实时用InfluxDB,分析用ClickHouse) 3. 连接池配置:pool_size=10~20,开启pool_pre_ping 4. 表结构设计:合理分区、索引、定期清理

嗯,数据库选型这块,说白了就是「看菜下饭」。别想着一个数据库打天下,该用MySQL就用MySQL,该上ClickHouse就上ClickHouse。我见过最惨的案例,有人用MySQL存Tick数据,三个月后查询一次要5分钟,最后不得不全量迁移。

记住:选型对了,后面省心十倍。


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