现场是这样的。DuckDB 1.0.0 装了 vss 扩展,对 200 万行 768 维的持久表执行 CREATE INDEX idx ON docs USING HNSW (embedding) WITH (metric='cosine'),跑到一半报 Out of Memory Error: failed to allocate 4.2 GiB,机器内存 16GB。
中间走过弯路,把「threads 默认等于核数,每个线程各留一块缓冲」当成主因,改完现象没变。
把日志和配置对完,能解释这个现象的基本就这几处。HNSW 构建全程在内存里,超出 memory_limit 直接失败;持久表建索引时原表和索引同时驻留,占用翻倍;metric 与数组类型不匹配,规划器走了更慢的路径。
先看当前内存上限和线程数
duckdb -c "select current_setting('memory_limit'), current_setting('threads');"
接着把内存上限和线程数压到可承受的范围
duckdb data.db -c "SET memory_limit='8GB'; SET threads=4; SET preserve_insertion_order=false;"
然后在小样本表上试建,确认参数可用
duckdb data.db -c "CREATE TABLE sample AS SELECT * FROM docs LIMIT 200000; CREATE INDEX sidx ON sample USING HNSW (embedding) WITH (metric='cosine');"
末了按实测规模重建正式索引并落盘
duckdb data.db -c "SET memory_limit='12GB'; CREATE INDEX idx ON docs USING HNSW (embedding) WITH (metric='cosine');"
别只看报错消失,验证要多做一步。建索引不再报 OOM,data.db 体积增大约 3GB,查询 order by array_distance 的耗时从全表扫描的 9 秒降到 120 毫秒。
DuckDB 的向量索引是纯内存构建,数据量上来后先算 向量数乘以维度乘以 4 字节 的底数,再留一倍余量,比事后调参靠谱。报错文案容易误导,真正的原因往往在相邻那一层的日志里。
