DuckDB参数调优咨询:内存配置与磁盘溢写疑问
问题背景
我有近800GB由维基百科转储解析得到的ndjson文件,需要按"url"分组、选取"dateModified"最新的"html"来去除重复HTML。针对16GB数据子集,我运行了以下代码:
import duckdb from pathlib import Path inDir = r"E:\Personal Projects\tmp\result" outDir = r"C:\Users\Akira\Documents\enwiktionary.ndjson" inDir = Path(inDir) outDir = Path(outDir) con = duckdb.connect() con.execute("SET threads=5") con.execute("SET memory_limit='12.5GB'") con.execute("SET preserve_insertion_order=false") result = con.sql(f""" COPY( SELECT arg_max(html, dateModified) as html FROM read_ndjson('{inDir / "*enwiktionary*.ndjson"}') GROUP BY url ) TO "{outDir}" """)
运行后出现如下错误:
--------------------------------------------------------------------------- OutOfMemoryException Traceback (most recent call last) Cell In[5], line 16 12 con.execute("SET memory_limit='12.5GB'") 13 con.execute("SET preserve_insertion_order=false") ---> 16 result = con.sql(f""" 17 COPY( 18 SELECT 19 arg_max(html, dateModified) as html 20 FROM read_ndjson('{inDir / "*enwiktionary*.ndjson"}') 21 GROUP BY url 22 ) 23 TO "{outDir}" 24 """) OutOfMemoryException: Out of Memory Error: failed to allocate data of size 16.0 MiB (11.6 GiB/11.6 GiB used) Possible solutions: * Reducing the number of threads (SET threads=X) * Disabling insertion-order preservation (SET preserve_insertion_order=false) * Increasing the memory limit (SET memory_limit='...GB') See also https://duckdb.org/docs/stable/guides/performance/how_to_tune_workloads
将"memory_limit"设为'13GB'时代码可正常运行。我的笔记本配备32GB内存、8核CPU(16线程),阅读官方内存管理文档后仍有以下疑问:
- DuckDB中哪些计算必须使用内存,无法溢写到磁盘?
- 针对当前数据集,如何调优
memory_limit参数?
解答
1. DuckDB中必须在内存执行的计算
- 哈希聚合构建阶段:执行
GROUP BY时,DuckDB会先构建哈希表分组数据,这个哈希表的核心结构必须留在内存,无法直接溢写到磁盘。如果分组基数过大,内存不足以容纳哈希表,就会触发OOM。 - 特定窗口函数计算:依赖全局排序或滑动窗口全量数据的窗口函数,部分中间结果无法溢出到磁盘,必须在内存中完成计算。
- 小数据量临时操作:极小数据集或临时计算会直接在内存处理,不会触发磁盘溢出逻辑——磁盘IO开销远大于内存处理,没必要溢出。
- 事务核心状态维护:事务的元数据、锁状态等核心信息必须在内存中维护,只有事务提交/回滚时才会持久化到磁盘。
2. 当前数据集的memory_limit调优建议
- 基准阈值参考:从16GB子集的测试来看,
13GB是刚好能运行的阈值。考虑到800GB全量数据的分组基数远大于子集,建议先将memory_limit设为20GB左右(预留12GB给系统和其他进程)。 - 动态调整上限:如果全量运行仍OOM,可逐步增加内存限制,但不要超过总物理内存的80%(你的32GB内存,最高设为
25GB左右),避免系统因内存耗尽卡顿或崩溃。 - 配合其他参数优化:
- 降低线程数到6-8:你的CPU是8核16线程,线程数设为核心数的1倍即可,过多线程会加剧内存竞争。
- 指定临时目录:开启
temp_directory参数,让DuckDB将可溢出的中间数据写到磁盘,减轻内存压力:SET temp_directory='D:/duckdb_temp'; - 分批次处理全量数据:按
url的哈希值拆分源文件,分批处理后合并结果,进一步降低单批次内存占用。
内容的提问来源于stack exchange,提问作者Akira
相关产品推荐
相关产品推荐

