在 Chroma 中,一个客户端对象对应一个连接池(HttpClient 模式)或者一个 SQLite 连接(PersistentClient 模式),每次创建和销毁它们要花费很大的代价,推荐在一个进程中,创建一次,后续复用。千万别在请求处理函数里反复 new 客户端,那会疯狂创建连接,放大连接耗尽和锁冲突的风险。
错误做法:每次请求都创建
import chromadb
def test(index:int) -> None:
client = chromadb.HttpClient(host="localhost", port=8000)
col = client.get_or_create_collection("demo", embedding_function=None)
col.add(ids=["q1", "q2"], documents=["如何进行退款?", "快递没有收到?"])
print(f"第{index}次: client={client}, {col.get()["documents"]}")
if __name__ == "__main__":
for i in range(10):
test(i + 1)运行输出如下:
第1次: client=<chromadb.api.client.Client object at 0x000002BE95CD6E40>, ['如何进行退款?', '快递没有收到?']
第2次: client=<chromadb.api.client.Client object at 0x000002BEA9713ED0>, ['如何进行退款?', '快递没有收到?']
第3次: client=<chromadb.api.client.Client object at 0x000002BEA9713ED0>, ['如何进行退款?', '快递没有收到?']
第4次: client=<chromadb.api.client.Client object at 0x000002BEA9C11810>, ['如何进行退款?', '快递没有收到?']
第5次: client=<chromadb.api.client.Client object at 0x000002BEA9C11220>, ['如何进行退款?', '快递没有收到?']
第6次: client=<chromadb.api.client.Client object at 0x000002BEA9C0FE30>, ['如何进行退款?', '快递没有收到?']
第7次: client=<chromadb.api.client.Client object at 0x000002BEA9C3D9D0>, ['如何进行退款?', '快递没有收到?']
第8次: client=<chromadb.api.client.Client object at 0x000002BEA9C3DAE0>, ['如何进行退款?', '快递没有收到?']
第9次: client=<chromadb.api.client.Client object at 0x000002BEA9C60950>, ['如何进行退款?', '快递没有收到?']
第10次: client=<chromadb.api.client.Client object at 0x000002BEA9C61050>, ['如何进行退款?', '快递没有收到?']从输出可知,每次调用都是一个全新的客户端对象,每次都要创建连接、销毁连接,这非常消耗资源。
正确做法:全局一个单例。
from functools import lru_cache
import os
import chromadb
@lru_cache(maxsize=1)
def get_client() -> chromadb.ClientAPI:
"""整个进程共用一个客户端"""
host = os.environ.get("CHROMA_HOST", "localhost")
port = int(os.environ.get("CHROMA_PORT", "8000"))
return chromadb.HttpClient(host="localhost", port=8000)
def test(index:int) -> None:
client = get_client()
col = client.get_or_create_collection("demo", embedding_function=None)
col.add(ids=["q1", "q2"], documents=["如何进行退款?", "快递没有收到?"])
print(f"第{index}次: client={client}, {col.get()["documents"]}")
if __name__ == "__main__":
for i in range(10):
test(i + 1)运行代码,输出如下:
第1次: client=<chromadb.api.client.Client object at 0x000001BB5AB16E40>, ['如何进行退款?', '快递没有收到?']
第2次: client=<chromadb.api.client.Client object at 0x000001BB5AB16E40>, ['如何进行退款?', '快递没有收到?']
第3次: client=<chromadb.api.client.Client object at 0x000001BB5AB16E40>, ['如何进行退款?', '快递没有收到?']
第4次: client=<chromadb.api.client.Client object at 0x000001BB5AB16E40>, ['如何进行退款?', '快递没有收到?']
第5次: client=<chromadb.api.client.Client object at 0x000001BB5AB16E40>, ['如何进行退款?', '快递没有收到?']
第6次: client=<chromadb.api.client.Client object at 0x000001BB5AB16E40>, ['如何进行退款?', '快递没有收到?']
第7次: client=<chromadb.api.client.Client object at 0x000001BB5AB16E40>, ['如何进行退款?', '快递没有收到?']
第8次: client=<chromadb.api.client.Client object at 0x000001BB5AB16E40>, ['如何进行退款?', '快递没有收到?']
第9次: client=<chromadb.api.client.Client object at 0x000001BB5AB16E40>, ['如何进行退款?', '快递没有收到?']
第10次: client=<chromadb.api.client.Client object at 0x000001BB5AB16E40>, ['如何进行退款?', '快递没有收到?']从输出可以看到,十次调用使用的是同一个客户端 0x000001BB5AB16E40。
注意,@lru_cache(maxsize=1) 是 Python 标准库 functools 提供的一个装饰器。它的核心作用是缓存函数的计算结果,以空间换时间,避免重复执行耗时的计算。
lru_cache:代表 Least Recently Used (最近最少使用) 缓存策略。当缓存满了,它会优先淘汰最久没有被访问过的数据。
maxsize=1:这是最关键的部分。它规定了缓存最多只能存放 1 组“参数-结果”的对应关系。