Chroma 教程

客户端共享与生命周期

🎉摘要:讲解Chroma客户端的正确使用方式:避免在请求中反复创建客户端,通过lru_cache实现单例复用,减少连接创建销毁开销,规避连接耗尽与锁冲突风险,附Python错误与正确实现代码对比。

在 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 组“参数-结果”的对应关系。

  

说说我的看法
全部评论()
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号