01-数据库选型说明

趋势定投分析的数据基础设施选型。核心判断:数据量在百 MB 级、读多写少、单机使用、零运维优先。SQLite 是最合适的选择。

选型结论

主库:SQLite(启用 WAL 模式)

理由概要:

  • 数据量在 50-500MB 级别,SQLite 舒适区
  • 站点分析以读取查询为主,每日批量更新写入,读多写少
  • Python 内置 sqlite3,零依赖零运维
  • 单文件,复制即备份,迁移成本低
  • 趋势定投不是量化交易,不需要毫秒级响应、高并发写入、分布式存储

为什么不选其他方案

DuckDB

DuckDB 是列式 OLAP 数据库,分析查询性能优于 SQLite,但本场景不选,原因:

  • 本项目数据量级下,SQLite 配合合理索引已足够快,DuckDB 的列式优势发挥不出来
  • DuckDB 生态较新,Python 集成需额外安装,不如 SQLite 开箱即用
  • 如果未来分析查询遇到性能瓶颈,DuckDB 可直接读取 SQLite 文件作为分析层补充,届时再加不迟

PostgreSQL / MySQL

关系型数据库服务端方案,本场景明显过重:

  • 需要安装和运维数据库服务,与"个人投资分析"的轻量定位不符
  • 单用户场景下,并发写入能力用不上
  • 备份迁移复杂度高于单文件方案

CSV / Parquet

文件方案查询能力弱:

  • 多表关联分析需要手动 join,维护成本高
  • 数据更新需要重写整个文件
  • 适合作为数据导出格式,不适合作为主存储

SQLite 配置说明

本项目采用以下配置,兼顾性能与可靠性:

import sqlite3

def get_connection(db_path: str) -> sqlite3.Connection:
    conn = sqlite3.connect(db_path)
    conn.row_factory = sqlite3.Row  # 行以字典形式访问
    conn.execute("PRAGMA journal_mode = WAL")      # WAL 模式,提升并发读
    conn.execute("PRAGMA foreign_keys = ON")        # 启用外键约束
    conn.execute("PRAGMA synchronous = NORMAL")     # 平衡性能与安全
    conn.execute("PRAGMA cache_size = -65536")      # 64MB 缓存
    conn.execute("PRAGMA temp_store = MEMORY")      # 临时表存内存
    return conn

各 PRAGMA 的作用:

PRAGMA作用
journal_modeWAL写前日志改为 WAL,读写可并发,读不阻塞写
foreign_keysON启用外键约束,保证引用完整性
synchronousNORMALWAL 模式下 NORMAL 足够安全,比 FULL 快
cache_size-65536负值表示 KB,64MB 内存缓存
temp_storeMEMORY临时表与中间结果存内存,加速复杂查询

版本管理策略

SQLite 数据库文件(.db)是二进制文件,直接进 git 会造成仓库膨胀且 diff 不可读。策略如下:

  1. .db 文件不进版本库.gitignore 排除 05-数据库/03-数据文件/*.db
  2. 建表脚本进版本库db.py 中的 schema 定义是版本管理的核心
  3. 数据快照可选进库:每月末导出一次 SQL dump(trend_invest_YYYYMM.sql.gz),可选提交到仓库或单独存储
  4. schema 版本号:在 db_meta 表记录 schema 版本,便于后续迁移
CREATE TABLE IF NOT EXISTS db_meta (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

INSERT OR REPLACE INTO db_meta (key, value, updated_at)
VALUES ('schema_version', '1.0.0', datetime('now'));

容量预估

数据库概览 定义的数据范围粗估:

数据类型标的数量历史深度行数估算存储估算
指数日线30成立日起全历史30 × 4000 ≈ 12 万8 MB
指数估值30成立日起全历史30 × 4000 ≈ 12 万12 MB
基金日线20成立日起全历史20 × 2500 ≈ 5 万4 MB
基金持仓205 年(季)20 × 20 = 400<1 MB
股票日线100上市日起全历史100 × 3000 ≈ 30 万24 MB
股票财报1005 年(季)100 × 20 = 20001 MB
行业日线305 年30 × 1250 = 3.75 万3 MB
宏观指标2020 年+(月)20 × 240 = 4800<1 MB
资金流向103 年(日)10 × 750 = 75001 MB

合计约 55 MB,加索引后约 80-120 MB。SQLite 单文件上限 140TB,完全不是瓶颈。

何时需要迁移

出现以下情况时,考虑迁移到 DuckDB 或 PostgreSQL:

  • 数据量超过 10GB(短期不会出现)
  • 查询响应超过 5 秒且索引优化无效
  • 需要多用户并发写入
  • 需要实时行情接入(本场景不涉及)

在可预见的趋势定投分析需求下,SQLite 至少够用 5 年。