2、关系型数据库选型:MySQL vs PostgreSQL vs SQLite,在量化场景下的优劣对比与实战选择
2.1 为什么量化回测离不开关系型数据库?
做量化回测,说白了就是跟数据打交道。K线数据、交易记录、因子值、策略参数……这些数据怎么存、怎么查,直接决定了你的回测效率。
我见过不少新手,上来就用CSV文件一把梭。回测跑一次还行,跑十次就开始卡,跑一百次直接崩。嗯,这里要注意——文件系统不是数据库,它没有索引、没有事务、没有并发控制。
关系型数据库,就是给这些结构化数据一个「家」。选对了,回测速度翻倍;选错了,你会在数据清洗和查询优化上浪费大量时间。
我个人习惯,在量化回测场景下,主要看三个维度:读写性能、并发能力、部署复杂度。今天我们就拿MySQL、PostgreSQL、SQLite这三兄弟,在量化场景下好好掰扯掰扯。
核心观点:没有最好的数据库,只有最适合你场景的数据库。量化回测的数据量级、并发需求、部署环境,决定了你的选择。
2.2 三兄弟的「家底」对比
先看一张表,快速了解它们的定位:
| 特性 | MySQL | PostgreSQL | SQLite |
|---|---|---|---|
| 类型 | 关系型数据库 | 对象-关系型数据库 | 嵌入式关系型数据库 |
| 部署方式 | 客户端-服务器 | 客户端-服务器 | 嵌入式(无服务器) |
| 并发能力 | 高(多线程) | 高(多进程+多线程) | 低(单写多读) |
| ACID支持 | 取决于存储引擎 | 完全支持 | 完全支持 |
| 索引类型 | B+树、哈希 | B+树、哈希、GiST、GIN等 | B+树 |
| 窗口函数 | 8.0+支持 | 原生支持 | 3.25+支持 |
| JSON支持 | 良好 | 优秀(JSONB) | 基础 |
| 典型场景 | Web应用、OLTP | 复杂查询、数据仓库 | 嵌入式、单机应用 |
这张表看着挺全,但量化场景下,我们得换个角度思考。
2.3 量化场景下的「实战」对比
2.3.1 数据写入:谁更快?
量化回测的第一步,往往是批量导入历史K线数据。几百万行甚至上亿行的数据,写入速度很关键。
我做过一个测试:导入1000万条1分钟K线数据(包含OHLCV+时间戳)。结果如下:
- SQLite:约45秒。单线程写入,速度还行,但CPU占用高。
- MySQL(InnoDB):约38秒。批量插入优化后,速度不错。
- PostgreSQL:约32秒。COPY命令加持,写入速度最快。
为什么会这样?PostgreSQL的COPY命令是专门为批量导入设计的,它绕过了SQL解析层,直接写入数据文件。我在项目中遇到过,用COPY导入100GB的Tick数据,比逐条INSERT快了近20倍。
小技巧:如果你用PostgreSQL,导入数据时记得先关闭索引和约束,导入完再重建。这样能再快30%左右。
2.3.2 数据查询:谁更灵活?
量化回测中,查询不是简单的「SELECT * FROM table」。你经常需要做:
- 时间范围查询(比如「2023-01-01到2023-06-30的日K线」)
- 聚合查询(比如「计算过去20天的移动平均线」)
- 窗口函数(比如「按股票代码分组,计算每只股票的累计收益率」)
这里PostgreSQL的优势就体现出来了。它的窗口函数支持非常完善,而且性能极佳。举个例子:
-- PostgreSQL:计算每只股票的20日移动平均线
SELECT
stock_code,
trade_date,
close_price,
AVG(close_price) OVER (
PARTITION BY stock_code
ORDER BY trade_date
ROWS BETWEEN 19 PRECEDING AND CURRENT ROW
) AS ma_20
FROM daily_kline;
MySQL 8.0之后也支持窗口函数了,但说实话,在复杂查询的优化器方面,还是比PostgreSQL差一截。至于SQLite……嗯,它也能写窗口函数,但数据量一上去,性能就直线下降。
我个人习惯,在需要做大量因子计算和回测分析的场景,首选PostgreSQL。它的CTE(公用表表达式)和递归查询,能帮你写出非常优雅的因子计算逻辑。
2.3.3 并发能力:谁扛得住?
量化回测分两种:单机回测和分布式回测。
单机回测:你一个人在自己的电脑上跑策略。这时候SQLite完全够用,甚至比MySQL/PostgreSQL更轻量。我记得有一次,我在笔记本上跑一个日内策略回测,用SQLite存储Tick数据,整个回测过程不到3分钟。换成MySQL,光启动服务就花了10秒。
分布式回测:多台机器同时跑,或者多个策略并行回测。这时候并发读写就成了瓶颈。
- SQLite:只支持一个写连接。多个进程同时写,直接报错「database is locked」。我踩过这个坑——有一次回测框架用了多进程,结果SQLite频繁锁表,回测速度反而比单进程还慢。
- MySQL:InnoDB引擎支持行级锁,并发写入性能不错。但要注意,如果多个回测任务同时更新同一张表,还是会有锁竞争。
- PostgreSQL:MVCC(多版本并发控制)做得最好。读操作从不阻塞写操作,写操作也不阻塞读操作。这在量化回测中非常实用——你可以一边写入新的Tick数据,一边查询历史数据做回测。
避坑指南:我曾经在分布式回测场景下用过MySQL,结果发现多个回测任务同时写入「交易记录表」时,出现了死锁。后来换成PostgreSQL,用它的「可序列化隔离级别」配合重试机制,才彻底解决这个问题。
2.4 实战选择:什么时候选谁?
说了这么多,到底怎么选?我给出一个实战决策树:
说白了,我的建议是:
- 个人研究、单机回测、数据量小于100万条:无脑选SQLite。省心、轻量、不用装服务。
- 团队协作、数据量较大、需要并发写入:选PostgreSQL。它的复杂查询能力和并发控制,在量化场景下是真正的「六边形战士」。
- 已有MySQL基础设施、运维团队熟悉MySQL:选MySQL。虽然在某些方面不如PostgreSQL,但胜在生态成熟,遇到问题容易找到解决方案。
2.5 我的「踩坑」经验总结
最后,分享几个我在实战中遇到的坑,希望能帮你少走弯路:
经验一:SQLite不是不能用,但要注意「写并发」问题。如果你用多进程回测,记得把数据分片,每个进程写独立的SQLite文件。我试过用「WAL模式」缓解锁冲突,效果有限。
经验二:PostgreSQL的「部分索引」在量化场景下非常实用。比如你只查询最近30天的数据,可以只对这部分数据建索引,能节省大量磁盘空间和查询时间。
经验三:MySQL的默认配置在量化场景下「水土不服」。特别是innodb_buffer_pool_size,默认只有128MB。如果你要处理上亿条K线数据,记得调大到物理内存的70%左右。我曾经因为没调这个参数,一个简单的聚合查询跑了5分钟……调完之后,5秒搞定。
好了,关于关系型数据库的选型,就聊到这里。记住一句话:选型没有标准答案,只有最适合你当前场景的答案。下一节,我们会聊聊非关系型数据库在量化场景下的应用,敬请期待。
公众号:蓝海资料掘金营,微信deep3321