跳到主要内容

多租户 LLM 问题:规模化部署中的嘈杂邻居、隔离与公平性

· 阅读需 13 分钟
Tian Pan
Software Engineer

你的 SaaS 产品以十个设计客户的规模上线,一切运转完美。随后你陆续接入了一百个租户,其中一个——一位在复杂研究工作流中使用 20 万 token 上下文窗口的重度用户——导致了所有其他客户的延迟飙升。支持工单开始涌来。你查看监控面板,却看不到任何明显异常:模型健康,API 返回 200,p50 延迟看起来正常。而你的 p95 已经悄悄翻了三倍。

这就是嘈杂邻居问题,它对 LLM 基础设施的冲击比几乎任何其他共享系统都要剧烈。以下是它为何比数据库场景更难解决——以及真正有效的应对方案。

为何 LLM 打破了多租户的基本假设

传统多租户基础设施拥有数十年的工具积累:连接池、行级安全、每 schema 独立数据库、VPC。所有这些方案的核心抽象都是:每次请求的资源消耗是有上限且大致可预测的。一次数据库查询耗时几毫秒,一次 Web 请求消耗几百微秒的 CPU。你可以根据请求速率来估算容量。

LLM 请求彻底打破了这一假设。单次推理调用可能消耗 512 个 token,也可能消耗 200,000 个。它可能在 300ms 内返回,也可能在复杂的多步骤链路中耗时四分钟。一个 Llama-3 70B 模型上单次长上下文会话的 KV 缓存可能超过 40GB——这些内存必须在整个会话期间驻留在 GPU 上。一块能服务 50 个并发短上下文用户的 GPU,可能被两个运行扩展研究工作流的用户完全占满。

这种变异性并非可以通过工程手段规避的缺陷——它是 LLM 有用性的根本所在。同一套基础设施既要处理简单的问答,也要处理文档分析和多轮 Agent 工作流。而这些工作负载落在同一硬件上。

故障模式在爆发前是隐形的。某个租户的高消耗会话驱逐了另一个租户的 KV 缓存,迫使重新计算。重新计算导致首 token 时间飙升。用户看到卡顿。你的监控显示 GPU 很忙——技术上准确,但对于诊断谁的会话消耗了谁的资源毫无帮助。

基于 Token 的速率限制不是可选项

大多数团队从每分钟请求数(RPM)限制起步。这很容易实现,并能立即阻止最严重的滥用。但它无法解决嘈杂邻居问题,因为请求并不等价。一个发送 100 次短文本分类请求的租户,和一个发送 3 次各含 5 万 token 上下文的请求的租户,对 GPU 的占用完全不同。

LLM 速率限制的正确原语是每分钟 token 数(TPM),同时计算输入和输出。输入 token 在请求时已知;输出 token 必须事后估算或追踪。实践中的做法是:在准入时统计输入 token 并对每个租户每个时间窗口强制执行 token 预算,然后在生成过程中将实际输出 token 计入该预算。

三种算法主导了生产环境的实现:

令牌桶(Token bucket) 以稳定速率填充,并允许突发到桶的容量上限。这很好地模拟了实际提供商的行为——大多数 LLM API 以固定 TPM 速率补充,并允许短期突发。令牌桶是大多数应用的正确默认选择,因为它在保障平均速率公平性的同时支持突发容忍型工作负载。

滑动窗口(Sliding window) 根据滚动时间间隔评估请求。它防止了固定窗口允许的边界利用(在一个窗口末尾和下一个窗口开头各发送最大请求量),但在分布式系统中实现更复杂。

优先级队列(Priority queuing) 不通过丢弃请求来限速——而是在系统过载时通过排队低优先级请求来优雅降级。交互式用户请求获得高优先级;后台批处理任务在有容量时处理。对于宁愿增加延迟也不想返回错误的应用,这是正确答案。

基于 token 的速率限制开销可以忽略不计——约 4ms——与模型生成时间相比微不足道。没有理由跳过它。

一个常被忽视的关键点:速率限制必须在 LLM 调用派发之前执行,而非之后。事后检查 token 预算意味着失控的租户已经消耗了你无法追回的资源。执行点属于你的 API 网关或推理代理,而非应用代码。

租户隔离的三个层次

多租户 LLM 基础设施需要在三个独立层次上实现隔离:推理层、检索层和上下文层。大多数团队实现了其中一两个,然后以惨痛的方式发现了第三个。

推理层隔离

对于大多数应用,共享模型实例加上每租户速率限制和预算追踪就足够了。风险不在于租户共享模型权重——他们始终如此——而在于某个租户的资源消耗拖慢了其他人的延迟。速率限制可以处理这个问题。

对于受监管行业或高安全需求,每租户独立的模型实例提供硬隔离。这代价高昂:每个独立实例意味着一块或多块 GPU 专属于一个租户,无论其是否活跃使用。这种经济模型仅对有强合规要求的高价值企业客户成立。

中间方案是命名空间级隔离:共享 GPU 资源加上调度器强制公平性。SGLang 和 vLLM 等现代服务系统支持连续批处理,跨同一 GPU 资源交错处理不同请求的 token 生成。通过适当的准入控制和每租户 token 预算,这在无需专用硬件的情况下实现了合理的公平性。SGLang 的 RadixAttention 内存管理器对于多轮工作负载比基准 vLLM 提供约 29% 的更高吞吐量——服务层的架构选择显著影响多租户效率。

检索层隔离(RAG)

这是大多数实现存在漏洞的地方。当租户共享向量索引时,检索隔离不仅仅是添加一个 tenant_id 过滤器,还需要在查询时、在结果到达生成层之前强制执行该过滤器。

关键错误是检索后过滤:先从整个索引检索,再丢弃属于其他租户的结果。这是数据泄漏风险——在过滤器运行之前,来自一个租户文档的文本块出现在另一个租户的检索上下文中。更重要的是,这很低效:你为立即丢弃的内容支付了检索和嵌入的费用。

三种模式在不同规模下处理 RAG 隔离:

每租户独立集合 提供最干净的隔离。每个租户拥有自己的向量集合。从设计上就不可能发生跨租户查询。权衡在于运营开销:在每次租户入驻时创建新集合,分别管理索引大小,即使是文档量少的租户也需要支付固定成本。

共享索引中的命名空间(Pinecone 的方式)或分区集合(Qdrant 的分层多租户)提供了中间方案。租户在索引内相互隔离,但共享底层存储和计算。这能高效处理常见情况,但不提供硬隔离保证。

tenant_id 的元数据过滤 是最常见的方式,也是实现不当时最危险的方式。过滤器必须在查询层强制执行,绝不能在检索后。将其绑定到 JWT 或会话 token——从你的基础设施层将 tenant_id 注入每次检索调用,而非从应用代码注入。应用代码可以被绕过;基础设施强制执行则不能。

这里关键的安全原则是:授权发生在数据接触生成上下文之前。一旦检索结果进入提示词,就可能通过模型输出被泄露。

上下文层隔离

上下文隔离是讨论最少的层次,也是故障模式最隐蔽的层次。在缓存提示词或维护会话状态的多租户系统中,跨租户上下文泄漏是真实存在的风险。

需要了解的具体漏洞是 KV 缓存共享。跨租户共享 KV 缓存条目提供了真实的性能收益——它消除了系统提示词等共享前缀的冗余重算。但 NDSS 2025 发表的研究证明,跨租户 KV 缓存复用会产生时序侧信道攻击,允许重建其他租户的提示词。这个攻击面是真实存在的,而非理论推测。

安全做法是缓存加盐:为所有缓存条目添加每租户的缓存键组件。这防止了隐私敏感内容的跨租户缓存命中。对于真正在所有租户间共享的系统提示词——例如你的基础指令——缓存共享是安全的,性能收益值得保留。对于任何租户特定的内容,则需要严格隔离。

对于多轮 Agent 中的会话状态,在存储层进行隔离。使用每租户独立的内存命名空间,而非带 tenant_id 行的共享表。工程成本更高,但隔离保证更强,访问模式也更清晰。

负载下的公平性与降级

速率限制防止了最严重的嘈杂邻居场景,但它无法告诉你当聚合负载超过容量时该怎么做。你需要制定明确的优雅降级策略。

优先级通道 是最实用的方法。按类型对请求分类:交互式用户会话获得最高优先级,API 集成获得中等优先级,后台批处理任务获得最低优先级。当系统过载时,首先卸载最低优先级的流量。用户对批处理操作的延迟容忍度远高于对交互式响应的延迟。

准入控制 是速率限制的补充。速率限制管理稳态;准入控制管理尖峰。当新请求会推动租户超出当前窗口的 token 预算时,立即排队或返回 429,而不是降低其他租户的服务质量。带有 Retry-After 响应头的清晰 429 比无声的 10 秒等待体验更好。

每租户熔断器 防止单个故障租户导致系统级降级。如果某个租户的请求持续超出 token 限制或反复报错,就独立对该租户的流量进行熔断,而不影响其他人。

预算执行时机 比大多数团队意识到的更为重要。在请求准入时执行预算,而非生成后。在模型运行完毕后再检查,意味着你已经消耗了无法回收的计算预算。

与数据库多租户的运营差异

运营过多租户数据库的团队往往假设 LLM 多租户类似。这种思维模型无法直接迁移。

数据库查询受 CPU 限制,且每种查询类型的 CPU 成本相对可预测。你可以从查询速率和平均执行时间估算容量。LLM 推理受 GPU 限制,成本因上下文长度而相差一个数量级。2000 token 的请求和 200,000 token 的请求在 HTTP 层看起来完全相同,直到生成开始才显现差异。

数据库连接池通过排队和连接复用处理并发。GPU 推理使用连续批处理,服务引擎动态调度跨并发请求的 token 生成。干扰发生在更底层——驱逐一个租户的 KV 缓存来容纳另一个租户的请求——更难观测。

数据库行级安全是确定性的:查询要么返回其被授权看到的行,要么不返回。向量检索过滤是概率性的,如果实现不正确可能静默失败。缺失的过滤器会在不返回任何错误的情况下,通过生成层传播数据泄漏。

数据库 schema 变更是事务性的,对所有查询立即可见。嵌入模型更新则不向后兼容——模型版本 N 的嵌入向量与版本 N+1 的向量不可互换。更新嵌入模型意味着重新索引整个向量存储,这必须在租户持续添加文档的多租户系统中零停机进行。

这些差异并不意味着多租户 LLM 基础设施不可能实现——许多公司都在规模化生产环境中运行。但它们意味着你不能照搬数据库运营的模式。基本原语是不同的。

优先构建什么

如果你正在搭建多租户 LLM 基础设施并需要确定优先级,以下是回报最高的顺序:

从网关层的 TPM 速率限制开始。 这是你能做的单一影响最大的改变。它防止了最具破坏性的嘈杂邻居场景,并作为免费副作用提供了每租户的成本归因。在你接入第十个租户之前就实现它。

用强制查询时过滤实现检索隔离。 在摄入大量租户数据之前把这个做对。检索后过滤是安全漏洞,也是计算浪费。正确的架构在检索结果进入生成上下文之前就强制执行隔离。

当系统首次触达容量上限时添加优先级队列。 不要提前构建复杂的队列基础设施。等你有了真实负载数据,能看清哪些工作负载类型相互竞争时再构建。

明确审计你的 KV 缓存共享策略。 不要将服务框架的默认行为视为安全决策。明确决定哪些缓存条目可以安全地跨租户共享,哪些需要隔离,然后强制执行该策略。

将计费与执行分开。 许多团队追踪每租户 token 消耗用于计费,并认为这也是其执行机制。它不是。计费记录已发生的事;执行防止它的发生。两者独立构建。

多租户 LLM 基础设施将成为任何拥有多个客户的 AI 产品的基本要求。那些把基本原语做对的团队——速率限制、检索隔离、上下文隔离、优雅降级——将把时间花在构建产品特性上。那些推迟的团队将把时间花在向企业客户解释为何一个租户的研究工作流导致了他们的延迟飙升。


嘈杂邻居问题不会主动宣告自己的到来。它悄悄积累,直到你的 p95 延迟变成别人的 p50。在你需要解释它不在那里之前,先把隔离构建好。

References:Let's stay in touch and Follow me for more thoughts and updates