DuckDB 数据导入管线的元数据设计与性能优化(zdq 量化项目)

AI 工作日志 作者头像野生码农 阅读 5 min
返回板块 GEO 源码全文 ✓
DuckDB 数据导入管线的元数据设计与性能优化(zdq 量化项目)

DuckDB 数据导入管线:元数据只保留 zip 级别,导入先预检查再处理

背景

zdq 项目(d:\code\python\zdq)的行情数据导入管线出现 15 分钟股票/指数数据为 0、导入脚本卡死、日周月脚本重复全量处理等一系列问题。本条目沉淀这次修复中确认的设计原则与排障方法。

核心内容

1. 元数据表只记 zip 级别,不记 zip 内部 csv(核心教训)

原 build_data_meta.py 为每个 zip 内的每个 csv 都建一条元数据记录(路径形如 xxx.zip#sh600000.csv),后果:

  • 15 分钟数据产生 1561 万条 csv 级记录,全库达 7789 万条
  • 查询待导入文件扫描全表,单次 41 秒;UPDATE 元数据全表扫描,每批次提交 3-4 分钟
  • csv 记录的 is_imported 永远不会被更新,脚本每次运行都重复匹配 → 重复导入

改为只保留 zip 级别后,15 分钟元数据从 1561 万条降到约 1.2 万条,查询/更新走主键毫秒级完成。原则:元数据粒度 = 实际导入处理的最小单元;zip 按整个文件导入,就只记 zip。

2. 用重建表代替大表 DELETE

清理 7790 万条冗余元数据时,重建表(复制有效记录建新表再替换)仅 3.8 秒,远快于 DELETE。DuckDB/列存场景下,删除大比例行优先用重建。

3. 幂等导入靠主键约束兜底

(trade_time, ts_code) 主键 + INSERT ... ON CONFLICT DO NOTHING 保证脚本重复运行不产生重复数据。验证方法:总行数 == 唯一键组合数。但这只保证正确性,不保证效率。

4. 导入前先预检查,避免"读取→拼接→注册→被主键拒绝"的空转

import_daily_weekly_monthly.py 原逻辑每次全量遍历 6059 个股票目录、读取所有 csv、拼 DataFrame,最后被主键全部拒绝。优化为先查目标表数据量/日期范围,已导入直接跳过,耗时从数十分钟降到 2 秒。

5. 元数据与导入脚本必须指向同一个库、同一套路径格式

这次事故的多个环节都源于不一致:build_data_meta.py 指向 data_meta.duckdb(F盘)而导入脚本用 stock_data.duckdb;元数据路径格式(csv 结尾)与导入脚本过滤条件(LIKE '%.zip')不匹配,导致 1561 万条记录全部漏导。改库名、改路径格式时,元数据生成端和消费端必须一起改。

6. 数据目录约定(zdq 项目)

  • 指数日月周K线数、A股沪深日月周K线数:csv 数据(指数原包为 zip,解压后有 _temp 目录需过滤)
  • A股_分时数据_沪深、A股_分时数据_指数:zip(按月归档),不要管内部 csv
  • build_data_meta.py 扫描时须过滤 _temp 临时目录

7. 排障方法要点

  • 脚本"卡死"不一定是卡死:本次真实原因是每批次后的元数据 UPDATE 扫描 1561 万条记录耗时 3-4 分钟,插入本身 12-13 万条/秒。先分层测试(读 zip → 插内存表 → 插磁盘表 → 元数据更新)定位真正慢的环节,别急着改主流程。
  • 插入性能参考:DuckDB 单表批量插入 100 万行约 1.44 秒,分钟级行情导入 13-21 万条/秒属正常。

原文链接

提炼自 inbox/trae/20260424_修复并导入15分钟数据.md

野生码农头像

关于作者 · 野生码农

把 AI 真放进生产环境的独立开发者。自研本地知识库、数字员工团队、软考备考系统、量化交易四套系统,全程公开复盘。

看他的平台账号 →
相关

继续阅读