You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

向Chroma DB导入预向量化数据时遇bad allocation错误的技术求助

解决Chroma向量数据库导入预向量化数据时的内存分配错误

问题描述

本地磁盘已有一个包含50万行数据的Chroma集合,在Jupyter Notebook中重新加载该集合并添加更多预向量化文本数据时,触发了bad allocation内存分配错误。推测是客户端无法将大文件加载到内存导致的,尝试过每批次导入后和单个10万行文件导入后执行persist(),但问题仍存在。需要优化导入和持久化的代码设计来避免该错误。


现有代码

客户端初始化及获取现有集合

client_wiki_ja = chromadb.Client(Settings(
    chroma_db_impl="duckdb+parquet",
    persist_directory= database_directory 
))

cohere_ef  = embedding_functions.CohereEmbeddingFunction(
    api_key=api_key_embedding_cohere, 
    model_name="multilingual-22-12")

collection_wiki_ja = client_wiki_ja.get_collection(name=collection_name,embedding_function=cohere_ef) # 从现有集合获取对象,不存在则抛出异常

数据导入函数

def load_files(batch_size=500,max_files_load=2,startFile_index=4): # startFile_index从0开始(例如train-00003-of-00035-99c97d6c1602f565.parquet对应startFile_index=3)
    
    directory = r"C:\Users\liewg\Desktop\p005-butumon-go\wikipedia-22-12-ja-embeddings\data" # 维基数据存放路径
    file_list = os.listdir(directory)
    counter = 0  # 已导入文件计数  

    for idx, filename in enumerate(file_list): # 遍历多个文件
        if idx < startFile_index:
            continue  # 跳过前几个文件
        
        file_path = os.path.join(directory, filename)  # 单个parquet文件路径  
        print(file_path)
        df = pd.read_parquet(file_path, engine='pyarrow')
        # df = df.head(3)
         

        df['emb'] = df['emb'].apply(lambda x: x.tolist())
        print("finished convert to list...")
        # print("number of files: ",df.count())

        rows = df# .head(10000)


        for i in tqdm(range(0, len(rows), batch_size)): # 单文件内分批次导入
            i_end = min(len(rows), i+batch_size) # 计算批次结束位置
            batch = rows[i:i_end]   

            embeds_batch = [ emb for emb in batch['emb']]
            text_batch = [ text for text in batch['text']]
            metadatas_batch = [ {"source": title} for title in batch['title']]
            ids_batch = [ str(id) for id in batch['id']]

            
            collection_wiki_ja.add(
                            embeddings= embeds_batch,
                            documents= text_batch,
                            metadatas=metadatas_batch,
                            ids = ids_batch
                    )
            print("batch:", i)
            print("persist starts")
            client_wiki_ja.persist()  # 每批次导入后持久化
            print("persist ends")

            # if i == 2: 
            #     break 
        print("completed")

        
        counter += 1
        if counter == max_files_load : 
            break 

错误信息

Error                                     Traceback (most recent call last)
Cell In[9], line 1
----> 1 load_files()

Cell In[8], line 42, in load_files(batch_size, max_files_load, startFile_index)
     40 print("batch:", i)
     41 print("persist starts")
---> 42 client_wiki_ja.persist()  # for after initial
     43 print("persist ends")
     45 # if i == 2: 
     46 #     break 

File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\chromadb\api\local.py:544, in LocalAPI.persist(self)
    536 @override
    537 def persist(self) -> bool:
    538     """Persist the database to disk.
    539 
    540     Returns:
    541         True if the database was persisted successfully
    542 
    543     """
--> 544     self._db.persist()
    545     return True

File ~\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\chromadb\db\duckdb.py:473, in PersistentDuckDB.persist(self)
    470 if self._conn.query("SELECT COUNT() FROM embeddings") == 0:
    471     return
--> 473 self._conn.execute(
    474     f"""```
    475     COPY
    476         (SELECT * FROM embeddings)
    477     TO '{self._save_folder}/chroma-embeddings.parquet'
    478         (FORMAT PARQUET);
    479     """
    480 )
    482 self._conn.execute(
    483     f"""
    484     COPY
   (...)
    488     """
    489 )

Error: Invalid Error: bad allocation

优化后的代码设计方案

核心优化思路

  1. 降低persist()调用频率:每批次调用会重复加载全量数据集写入磁盘,改为每导入完1个或多个文件后执行一次,减少内存重复占用。
  2. 分块读取文件:避免一次性加载整个Parquet文件到内存,用分块读取逐步处理。
  3. 限制DuckDB内存:通过配置限制DuckDB的内存使用上限,防止其耗尽系统内存。
  4. 主动清理内存:处理完数据块后主动删除临时变量并触发垃圾回收,释放内存。

优化后的代码

1. 客户端初始化(增加内存配置)

from chromadb.config import Settings
import chromadb
from chromadb.utils import embedding_functions
import pandas as pd
import os
from tqdm import tqdm
import gc

# 初始化客户端时配置DuckDB内存上限,根据系统内存调整(例如设为8GB)
client_wiki_ja = chromadb.Client(Settings(
    chroma_db_impl="duckdb+parquet",
    persist_directory=database_directory,
    duckdb_settings={"memory_limit": "8GB"}
))

cohere_ef = embedding_functions.CohereEmbeddingFunction(
    api_key=api_key_embedding_cohere, 
    model_name="multilingual-22-12")

collection_wiki_ja = client_wiki_ja.get_collection(name=collection_name, embedding_function=cohere_ef)

2. 优化后的导入函数

def load_files(batch_size=1000, max_files_load=2, startFile_index=4, persist_interval=1):
    directory = r"C:\Users\liewg\Desktop\p005-butumon-go\wikipedia-22-12-ja-embeddings\data"
    file_list = os.listdir(directory)
    counter = 0

    for idx, filename in enumerate(file_list):
        if idx < startFile_index:
            continue
        
        file_path = os.path.join(directory, filename)
        print(f"Processing file: {file_path}")
        
        # 分块读取Parquet文件,避免一次性加载全量数据
        chunk_iter = pd.read_parquet(file_path, engine='pyarrow', chunksize=batch_size*5)
        
        for chunk_df in tqdm(chunk_iter):
            # 转换emb为列表
            chunk_df['emb'] = chunk_df['emb'].apply(lambda x: x.tolist())
            
            # 再分小批次导入
            for i in range(0, len(chunk_df), batch_size):
                i_end = min(len(chunk_df), i + batch_size)
                batch = chunk_df[i:i_end]
                
                embeds_batch = batch['emb'].tolist()
                text_batch = batch['text'].tolist()
                metadatas_batch = [{"source": title} for title in batch['title']]
                ids_batch = batch['id'].astype(str).tolist()
                
                # 批量添加数据
                collection_wiki_ja.add(
                    embeddings=embeds_batch,
                    documents=text_batch,
                    metadatas=metadatas_batch,
                    ids=ids_batch
                )
            
            # 处理完一个大chunk后清理内存
            del chunk_df
            gc.collect()
        
        print(f"Completed processing file: {filename}")
        
        # 每导入persist_interval个文件后执行一次持久化
        counter += 1
        if counter % persist_interval == 0:
            print("Starting persist...")
            client_wiki_ja.persist()
            print("Persist finished")
        
        if counter == max_files_load:
            break
    
    # 最后执行一次持久化,确保所有数据写入磁盘
    print("Final persist...")
    client_wiki_ja.persist()
    print("All data imported and persisted")

额外建议

  • 调整batch_size:根据系统内存灵活调整,内存充足可增大到2000,内存紧张则缩小到300。
  • 使用64位Python:32位Python有内存上限限制,必须使用64位版本处理大数据集。
  • 监控内存占用:导入过程中用任务管理器监控内存使用,及时调整分块大小和批次参数。
  • 关闭Jupyter自动保存:暂时关闭Jupyter的自动保存功能,减少额外内存消耗。

内容的提问来源于stack exchange,提问作者Mike

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 11:28:09