存储格式与查询
个人与小团队的最佳实践
Section titled “个人与小团队的最佳实践”不需要数据库服务。现代分析栈的最小组合是:Parquet 分区存储 + DuckDB/Polars 直接查询——零运维、版本可控、读写快一到两个数量级。
| 格式 | 优点 | 缺点 | 适用 |
|---|---|---|---|
| CSV | 万能、可读 | 慢 10–100 倍、无类型、体积大 | 只做交换格式,不做工作格式 |
| HDF5 | 单文件、MATLAB 友好 | 并发写差、目录级分区难 | 已逐步被 Parquet 取代 |
| Parquet | 列式、压缩、谓词下推、分区 | 不可读、需按目录组织 | 默认选择 |
| Arrow | 内存列式、零拷贝 | 是传输/内存格式而非落盘格式 | 进程间交换 |
时序数据按 year=/month= 分区最实用:
data/ market/ daily/ year=2024/month=01/part-000.parquet year=2024/month=02/part-000.parquet fundamental/ announcements/ year=2024/part-000.parquet meta/ symbols.parquet # 证券主表 corporate_actions.parquet # 复权因子分区列不要用高基数列(如股票代码做顶层分区会产生几千个目录);查询时用谓词下推只扫描需要的月份。
DuckDB:直接查询 Parquet 目录
Section titled “DuckDB:直接查询 Parquet 目录”import duckdb
con = duckdb.connect() # 无需服务,直接查询文件
# 只扫描 2024 年 1 月的分区,谓词下推生效df = con.execute(""" SELECT symbol, date, close, volume FROM read_parquet('data/market/daily/year=2024/month=01/*.parquet') WHERE symbol LIKE '6005%' ORDER BY date""").df()
# 也可以把整张表注册后用 SQL 做复权、对齐等管道步骤con.register("raw_daily", df_raw)Polars 的等价工作流:pl.scan_parquet(...) 惰性扫描 + filter 谓词下推 + collect,与 DuckDB 二选一即可。
一致性与版本
Section titled “一致性与版本”- 落盘数据是不可变的:修正用新分区覆盖写入 + 版本号,不原地改,与复现性的数据快照配合;
- schema 显式声明(列名、类型、时区),跨年不漂移;
- 大于单机的 tick 数据才考虑 ClickHouse 等列式数据库服务。
量化中的用法
Section titled “量化中的用法”- 原始数据按日期分区存 Parquet,查询走 DuckDB/Polars;
- 复权因子、公告日等维表单列存放,与行情按 as-of 对齐;
- CSV 只在交付或交换时使用。
- Parquet + 分区 + 谓词下推,扫描速度比 CSV 快一到两个数量级
- DuckDB 可直接 SQL 查询 Parquet 目录,无需数据库服务
- 时序数据按
year=/month=分区最实用,顶层分区避免高基数列