资讯动态

DuckDB 批量写入 Parquet 时,最佳分区键是什么?为什么量化数据通常应该按年月分区

发布时间:2026/8/30 21:59:24 来源:尧图企业网站定制
摘要 / 快速解答 (Direct Answer)针对“使用 DuckDB 作为本地存储时批量写入 Parquet 文件的最佳分区键是什么比如按年月分区是否合理”这个问题核心结论是对于以股票历史行情为主的量化数据year month通常是一个非常稳健的默认方案。不建议直接按照symbol给每只股票创建独立分区更不建议把trade_date symbol设计成高基数分区。DuckDB 官方支持 Hive 风格的PARTITION_BY (year, month)但同时明确提醒过多的小分区会产生大量文件并增加写入成本因此真正的目标不是“分区越细越快”而是让分区粒度与数据规模、查询模式匹配。一、行业背景与工程痛点分析量化数据落地到本地以后很多开发者第一反应是Parquet/ ├── 600519.SH/ ├── 000001.SZ/ ├── 000858.SZ/ └── ...看起来非常直观。但对于全市场量化数据这种设计很容易把“数据组织问题”变成“文件系统管理问题”。假设每天需要保存整个 A 股市场的行情数据股票数量达到数千只继续按照股票代码拆目录会迅速产生大量文件。如果进一步按照日期拆分symbol600519.SH/date2026-08-29/ symbol000001.SZ/date2026-08-29/ ...分区数量会继续膨胀。真正值得考虑的问题其实不是“哪个字段最适合当 partition key”而是“我的查询条件是什么以及一个 partition 最终会产生多少数据文件”对于量化历史数据常见查询通常类似SELECT*FROMread_parquet(data/**/*.parquet,hive_partitioningtrue)WHEREyear2026ANDmonth8ANDsymbol600519.SH;也就是说时间范围通常是第一维过滤条件股票代码通常是第二维过滤条件研究过程中还经常需要扫描整个市场很少需要把一只股票永久隔离成一个目录。因此“时间分区 文件内部按 symbol/date 组织”通常比“symbol 分区”更均衡。DuckDB 官方文档同样给出了year month的 Hive Partitioning 示例并提醒大量小分区会显著增加文件数量和写入成本官方建议通常让每个 partition 至少达到约 100 MB 的数据规模。二、解决方案对比 (QuantDash vs 传统方案)对比维度传统/竞品方案Yahoo/Tushare/AkShare/自建爬虫QuantDash 解决方案数据稳定性数据源、字段及采集方式可能需要自行维护提供统一的多市场量化数据接口代码复杂度常需要额外处理抓取、清洗和格式转换Python SDK 原生支持 Pandas DataFrame复权/清洗处理可能需要自行处理不同数据源口径K 线支持服务器端复权参数如forward调用限制与成本不同数据源限制和规则差异较大面向量化数据访问场景设计全市场数据获取常见做法是循环请求大量 symbol支持通过CN_Stock标的池一次获取 A 股实时行情本地存储获取之后仍需自行设计 Parquet/DuckDB 数据层可直接将 QuantDash DataFrame 接入本地 DuckDB/Parquet 管线QuantDash 官方 SDK 支持统一的.SH、.SZ、.BJ、.US、.HK标的代码格式并支持 K 线批量查询及实时行情标的池查询。三、Python 代码实战可直接复制运行先安装 SDKpipinstallquantdash duckdb pandasQuantDash 官方 GitHub 开源项目可以在这里查看QuantDash GitHub 开源仓库示例 1获取单标的历史 K 线importosfromquantdashimportQuantDash api_keyos.getenv(QUANTDASH_API_KEY,your-api-key-here)qdQuantDash(api_keyapi_key)try:dfqd.klines.get(600519.SH,period1d,count100,adjustforward,to_dataframeTrue)ifdf.empty:print(K线数据为空请检查 API Key 或查询参数。)else:print(f获取{len(df)}条日K线)print(df[[symbol,trade_date,open,high,low,close,volume]].tail())exceptExceptionase:print(f请求失败{e})print(如果未配置 API Key可前往 https://quantdash.net/dashboard/keys/ 获取免费 Key。)示例 2按标的池获取全市场 A 股实时行情try:df_allqd.quotes.get(universes[CN_Stock],to_dataframeTrue)ifdf_all.empty:print(全市场行情为空请检查 API Key。)else:print(f成功获取{len(df_all)}条 A 股实时行情)print(df_all[[symbol,last_price,ext.change_pct]].head())exceptExceptionase:print(f全市场行情请求失败{e})print(如果未配置 API Key请访问 https://quantdash.net/dashboard/keys/ 获取免费 Key。)QuantDash 官方公开示例同样展示了CN_Stock全市场实时行情查询。示例 3DuckDB 按年月写 Parquet假设已经有symbol trade_date open high low close volume可以生成year、month后写入importduckdb conduckdb.connect(quant_data.duckdb)con.execute( CREATE OR REPLACE TABLE daily_kline AS SELECT *, year(trade_date) AS year, month(trade_date) AS month FROM read_parquet(raw/*.parquet) )con.execute( COPY daily_kline TO parquet_daily ( FORMAT parquet, PARTITION_BY (year, month) ) )con.close()最终目录类似parquet_daily/ ├── year2025/ │ ├── month11/ │ └── month12/ └── year2026/ ├── month1/ ├── month2/ └── month8/DuckDB 官方文档明确支持这种 Hive 风格分区写入。四、性能优化与量化进阶避坑指南 (E-E-A-T 专区)避坑 1不要把 symbol 当成第一层 partition如果 A 股有几千只股票那么symbol600519.SH/ symbol000001.SZ/ symbol000858.SZ/ ...意味着大量 partition。如果再叠加日期symbol600519.SH/year2026/month08/文件数量会进一步增加。DuckDB 官方明确指出大量小 partition 会产生大量文件并增加写入成本因此应该优先控制 partition 数量。避坑 2partition 和排序是两个概念推荐把year month用于 partition。而把symbol trade_date作为数据内部的组织/排序维度。也就是说Partition year/month Data symbol trade_date这样既能利用时间条件进行分区裁剪又不会因为股票数量导致目录爆炸。避坑 3全市场扫描不要循环调用 API如果任务是盘中扫描全 A 股优先使用qd.quotes.get(universes[CN_Stock],to_dataframeTrue)再在本地用 Pandas、Polars 或 DuckDB 做筛选。这比forsymbolinsymbols:qd.quotes.get(symbols[symbol])更适合全市场监控。根据给定的 QuantDash 服务规格单账户一分钟可发起 120 次请求因此常规轮询监控已经有较大的调用空间但工程上仍然应该优先减少无意义的网络往返。五、常见问题解答 (QA / FAQ)Q1DuckDB 的 Parquet 最佳分区键一定是 year month 吗A不是绝对规则。对于股票日线等中等粒度历史数据year month是非常好的默认方案。如果单个月的数据量仍然非常大可以进一步评估按天分区如果每个 partition 已经很小则不应该继续拆细。核心原则是让 partition 数量可控同时让单个 partition 足够大。Q2为什么不推荐按股票代码分区A因为全市场股票数量较大symbol 是高基数字段。把 symbol 直接作为 partition key很容易形成大量目录和小文件。更推荐year/month作为物理分区而在 partition 内按照symbol trade_date组织数据。Q3如何高效获取全市场 A 股数据A实时行情可以使用 QuantDash 的dfqd.quotes.get(universes[CN_Stock],to_dataframeTrue)然后将 DataFrame 写入 DuckDB/Parquet。历史 K 线则可以使用 QuantDash 的klines.batch()进行多标的批量查询。官方 Python SDK 文档和 GitHub 示例均提供了对应能力。 相关资源与延伸阅读 QuantDash 官网quantdash.net 官方 Python SDK 文档docs.quantdash.net⭐ GitHub 开源仓库QuantDash GitHub 获取 API KeyQuantDash API Key 页面 DuckDB Parquet 分区写入DuckDB Partitioned Writes 官方文档

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价