生产环境下 LLM 与向量数据库系统的扩缩容实战

作者
  • avatar
    姓名
    Nino
    职业
    Senior Tech Editor

构建一个检索增强生成(RAG)系统的原型其实非常简单。借助 LangChain 或 LlamaIndex 等框架,配合几个 API 密钥,你可以在一个下午的时间里搭建出一个功能完备的演示 Demo。然而,从一个“理想路径”的原型转变为能够处理数百个并发用户的生产级系统,正是大多数工程团队面临巨大挑战的开始。

最近,我们经历了一次重大功能的发布。起初,我们的 RAG 实现表现得非常完美:低延迟、高相关性,利益相关者也非常满意。但当一个合作伙伴的集成导致我们的流量一夜之间翻倍时,系统并没有只是变慢,而是陷入了级联故障。我们看到尾部延迟(tail latency)飙升,重试风暴瘫痪了我们的嵌入(Embedding)流水线,云端账单以不可持续的速度膨胀。

本文将详细介绍在扩容 LLM 和向量数据库系统过程中总结出的实战经验。通过使用 n1n.ai 这样的高性能 API 聚合器,我们成功稳定了推理层,但其周边的基础设施仍需要根本性的架构调整。

生产环境失败的深度剖析

事后看来,那次导致系统崩溃的事故是完全可以预见的。随着流量的激增,我们的同步请求路径(包括 Embedding 生成、向量索引和 LLM 提示词组装)成为了严重的瓶颈。

  1. Embedding 瓶颈:我们触及了 Embedding 供应商的频率限制(Rate Limits)。由于 Embedding 是在请求路径中同步生成的,供应商返回的每一个 429 错误都直接导致了用户的 5xx 错误。
  2. 向量数据库重平衡:我们的向量数据库配置了自动扩容。在繁重的写入压力下(因为我们需要不断摄取新文档),集群开始重新平衡分片(Shards)。这种重平衡导致近似最近邻(ANN)查询延迟从 50ms 飙升至 2s 以上。
  3. 平均值的陷阱:我们的仪表盘显示“平均延迟”为 800ms,看起来还可以接受。然而,我们的 p99 延迟超过了 15 秒。这意味着绝大多数用户体验到的其实是一个破碎的产品,而我们的高层监控指标看起来却“正常”。

为韧性而设计:转向异步架构

我们做出的最重大改变是将写入路径与读取路径解耦。在初级的 RAG 设置中,为了确保数据的“新鲜度”,开发者通常会在同一流程中索引数据并查询数据。在生产环境中,这是灾难的根源。

我们将所有的文档摄取和 Embedding 生成迁移到了基于消息队列的异步流水线中。当新文档到达时,我们立即确认收到并将其推入队列。工作线程池随后负责处理 Embedding 生成,这里我们使用了 n1n.ai 提供的稳定端点。

专家建议:接受“最终一致性”(即文档可能需要 30-60 秒才能变得可搜索)是为查询稳定性提升 10 倍而付出的微小代价。

优化 Embedding 流水线

Embedding 供应商通常对每分钟请求数(RPM)有严格限制。如果你对每个文档都发送一次 API 请求,很快就会触及这些限制。

我们实施了微批处理(Micro-batching)。不再是每个文档调用一次 API,而是将文档分组为 16 或 32 个一组(取决于模型的 Token 限制)。这减少了 HTTP 握手的开销,并最大化了吞吐量。

# 带有抖动指数退避的简化批处理逻辑示例
import time
import random

def get_embeddings_with_retry(text_batch):
    max_retries = 5
    for i in range(max_retries):
        try:
            # 使用 n1n.ai 进行统一的 LLM/Embedding 访问
            return call_embedding_api(text_batch)
        except RateLimitError:
            # 指数退避 + 随机抖动
            wait = (2 ** i) + random.random()
            time.sleep(wait)
    raise Exception("超过最大重试次数")

通过利用 n1n.ai,我们还可以在不同的 Embedding 模型之间进行故障转移(例如从 OpenAI 切换到本地托管模型),而无需更改核心集成逻辑。

向量数据库调优:冷热分层策略

向量数据库是内存密集型的。将数百万个高维向量存储在 RAM 中非常昂贵。我们发现 90% 的查询仅针对最近 10% 的数据。

我们实施了分层存储策略:

  • 热层(Hot Tier):最近的和高频访问的文档存储在内存优化型节点中。这些节点针对低延迟 ANN 搜索进行了调优。
  • 冷层(Cold Tier):较旧的文档被移动到磁盘存储。我们接受这些查询的较高延迟,或者在进行向量检索之前,使用元数据过滤来缩小搜索范围。

元数据预过滤:速度的关键

性能提升最大的优化之一来自于元数据过滤。在执行向量相似度搜索之前,我们会根据 tenant_id(租户 ID)、timestamp(时间戳)或 document_type(文档类型)应用硬性过滤。

如果你有 1000 万个向量,但一个用户只能访问其中的 1000 个,那么搜索整个 1000 万向量的索引是非常浪费的。通过先应用元数据过滤,向量引擎只需要对极小的一个子集执行 ANN,从而将 p99 延迟降低了几个数量级。

超越 LLM 的可观测性

你无法管理你无法测量的事物。我们不再将“LLM 延迟”作为一个单一的整体来监控。相反,我们对流水线的每个阶段都进行了埋点:

  1. 预处理:清洗和切分文本的时间。
  2. Embedding:调用 Embedding API 所花费的时间。
  3. 向量检索:ANN 查询所花费的时间。
  4. 上下文组装:对检索到的块进行排序和格式化的时间。
  5. LLM 推理:生成最终响应的时间。

我们发现,在许多情况下,使用 DeepSeek-V3 或 Claude 3.5 Sonnet 等模型的 LLM 推理实际上是整个栈中最稳定的部分。主要的“噪音”来自于检索阶段。通过跟踪每个阶段的 p99 延迟,我们能够在向量数据库出现问题并影响总响应时间之前,就及早发现隐患。

成本控制与租户配额

在多租户环境中,一个“超级用户”可以轻易消耗掉你的全部 API 配额或导致成本飙升。我们在租户级别实施了软限制和硬限制。

此外,我们对确定性查询使用了提示词缓存(Prompt Caching)。如果同一公司的三个用户询问关于同一份政策文档的相同问题,我们会直接提供第一次查询的缓存结果。这不仅节省了资金,还为重复查询提供了低于 100ms 的响应时间。

总结:工程化的“管线”建设

扩容一个 LLM 产品,核心不在于“AI”,而在于“工程”。它关乎系统的解耦、状态的管理以及对尾部延迟的观察。通过转向异步架构、优化 Embedding 批处理,并利用像 n1n.ai 这样健壮的 API 网关,你可以构建出不仅能在演示中运行,而且能在真实流量压力下茁壮成长的系统。

停止让你的写入阻塞读取,开始为 p99 而设计,而不是为了平均值。

立即在 n1n.ai 获取免费 API 密钥。