背景:微调模型变多以后,瓶颈从训练转向服务
LoRA(Low-Rank Adaptation)大幅降低了大模型定制成本。一个基础模型可以对应几十、几百甚至上千个业务适配器——客服语气、行业术语、企业知识、代码风格、销售话术、合规回答边界,都可以通过不同的 LoRA 适配器承载。
问题是,训练出很多适配器并不等于可以低成本服务很多适配器。生产系统真正面对的是:
- 多个租户共享同一个基础模型;
- 每个租户可能使用不同 LoRA;
- 请求分布有明显冷热差异;
- 适配器 Rank、大小、加载时间不一致;
- GPU 显存同时要容纳基础模型、KV Cache、计算 buffer 和 LoRA 权重;
- 热点租户不能被冷门适配器的加载阻塞。
因此,Multi-LoRA Serving 的核心不是“能不能加载 LoRA”,而是如何把适配器当作一种可调度、可缓存、可驱逐的在线资源。
核心原理:共享基础模型,按请求激活适配器
普通部署方式会把每个定制模型当作一个完整模型实例。如果有 100 个租户,即使它们都基于同一个 Llama、Qwen 或 Mistral 基座,也可能重复加载大量相同权重。这种方式简单,但显存浪费严重,冷启动慢,扩缩容也粗糙。
Multi-LoRA 的思路是:
- GPU 常驻一份基础模型权重;
- LoRA 适配器以小权重增量形式存储;
- 请求进入时携带
adapter_id; - 推理引擎在执行对应层时,把适配器增量作用到基础模型计算中;
- 调度器允许不同
adapter_id的请求在同一服务中并发执行。
以 vLLM 为例,它已支持在 OpenAI-compatible server 中通过 --enable-lora 和 --lora-modules 启用 LoRA,请求可以像指定普通 model 一样指定 LoRA 适配器。它还提供 max_loras、max_lora_rank、max_cpu_loras 等配置,说明 LoRA 在服务端已不是单纯的模型文件,而是运行时资源池的一部分:
vllm serve meta-llama/Llama-3.2-3B-Instruct \
--enable-lora \
--lora-modules tenant-a=/models/lora/tenant-a tenant-b=/models/lora/tenant-b \
--max-lora-rank 64
为什么适配器缓存很关键
当适配器数量较少时,全部放入 GPU 显存看起来最简单。但适配器数量增加后,这种方式会遇到三个核心问题:
第一,显存占用不可控。 LoRA 权重虽然比完整模型小得多,但几百个适配器叠加后仍会挤占 KV Cache 空间,导致并发下降或长上下文能力退化。
第二,加载抖动影响 TTFT。 如果请求命中一个不在 GPU 中的适配器,系统需要从本地磁盘、对象存储、CPU 内存或远端节点加载,首 token 延迟(TTFT)会被明显拉长。
第三,调度器容易出现队头阻塞。 如果批次中混入需要加载冷适配器的请求,热适配器请求也可能被拖慢。
因此,生产系统需要把适配器分为以下几层:
| 层级 | 位置 | 适合对象 | 目标 |
|---|---|---|---|
| Hot | GPU memory | 高频租户、低延迟租户、近期持续活跃适配器 | 最小化 TTFT |
| Warm | CPU memory / pinned memory | 中频适配器、可接受轻微加载延迟的租户 | 降低磁盘和网络加载 |
| Cold | SSD / object storage | 低频租户、归档版本、灰度候选版本 | 降低长期存储成本 |
| Disabled | 不可服务状态 | 过期、撤回、未审核、风险版本 | 保证治理边界 |
驱逐策略:不能只用 LRU
很多系统自然想到 LRU(Least Recently Used),但 Multi-LoRA 场景仅靠 LRU 是不够的。原因在于不同适配器的 Rank、大小、加载成本、租户等级、SLO 要求差异巨大。
更合理的驱逐分数可以结合以下因素:
eviction_score = w1 × idle_time
+ w2 × memory_size
+ w3 × load_cost
- w4 × recent_hit_count
- w5 × tenant_priority
- w6 × predicted_near_future_demand
其中各因子含义:
| 因子 | 含义 |
|---|---|
idle_time | 多久没有被请求命中 |
memory_size | 适配器显存占用,Rank 越大通常越贵 |
load_cost | 从 warm/cold 层恢复的耗时 |
recent_hit_count | 近期访问频率 |
tenant_priority | 高等级客户不应轻易被驱逐 |
predicted_near_future_demand | 基于时间段、任务队列或历史规律预测即将到来的请求 |
工程上可以先从简单版本做起:
- Hot 层设显存水位,例如不超过可用显存的 10%–20%;
- 以
adapter_id维护命中次数、最近访问时间、加载耗时、当前 inflight 请求数; - 当 Hot 层超过水位,优先驱逐低频、低优先级、无 inflight 的适配器;
- 对高 Rank 适配器设置更严格的缓存预算;
- 对高价值租户设置最小驻留时间,避免频繁抖动。
Rank 感知调度:不同适配器不能完全等价
LoRA 的 Rank 直接影响额外计算和内存占用。vLLM 文档也提醒,--max-lora-rank 不应该设置得远高于实际适配器 Rank,否则会浪费内存并影响性能。
生产系统中至少要为每个适配器记录如下元数据:
adapter_id: tenant-a-support-v3
base_model: qwen3-32b
rank: 32
size_mb: 180
status: active
storage_uri: s3://llm-adapters/tenant-a/support/v3/
slo_class: gold
warmup_policy: preload_on_business_hours
max_concurrent_requests: 32
调度器可以根据 Rank 和租户等级做分组:
- 小 Rank 高频适配器:适合放入热队列,保持高并发;
- 大 Rank 高频适配器:需要单独预算,避免挤占所有显存;
- 冷门适配器:允许排队加载,不进入核心低延迟队列;
- 灰度适配器:限制并发,观察错误率和质量指标后再放量。
这类策略与 S-LoRA、Punica、Chameleon、CaraServe 等研究方向一致:它们都不是简单地“加载 LoRA”,而是在适配器存放、批处理、GPU/CPU 协同、Rank 差异和调度策略上做系统优化。
动态加载的安全边界
动态加载 LoRA 很方便,但不能把 /v1/load_lora_adapter 这类能力直接暴露给不可信调用方。vLLM 文档明确提示,动态 LoRA 配置存在安全风险,不应在非隔离、非完全可信的生产环境中使用。
生产环境至少需要以下边界:
adapter_id必须来自内部注册表,不能由用户直接传任意路径;storage_uri必须在白名单存储桶和路径前缀下;- 适配器文件需要签名、哈希校验和版本记录;
- 加载、卸载、替换必须有审计日志;
- 适配器状态要经过 uploaded → validated → staged → active → disabled 流程;
- 推理请求只能选择当前租户有权限访问的
adapter_id; - 适配器替换需要灰度发布和回滚机制。
工程落地架构
一个可维护的 Multi-LoRA 服务可以拆成五个模块:
1. Adapter Registry(适配器注册中心)
负责保存适配器元数据:租户、基础模型、Rank、版本、状态、存储地址、校验哈希、发布时间、回滚版本。
2. Adapter Cache Manager(适配器缓存管理器)
负责 Hot / Warm / Cold 分层、命中统计、加载、卸载、驱逐和预热。它需要暴露以下关键指标:
| 指标 | 含义 |
|---|---|
adapter_cache_hit_ratio | 适配器缓存命中率 |
adapter_gpu_resident_count | GPU 常驻适配器数量 |
adapter_load_latency_ms | 适配器加载延迟 |
adapter_eviction_count | 适配器驱逐次数 |
adapter_memory_bytes | 适配器显存占用 |
cold_adapter_request_count | 冷适配器请求数量 |
3. Request Router(请求路由器)
根据 tenant_id、task_type、model_alias 解析出 adapter_id,并检查权限、状态和灰度比例。
4. Scheduler(调度器)
做 Rank 感知、租户公平性、队列隔离、加载中请求合并。多个请求同时命中同一个冷适配器时,不应重复加载,而应合并等待同一次加载结果。
5. Release Controller(发布控制器)
负责适配器版本发布、灰度、回滚、禁用和审计。适配器本质上是线上模型能力的一部分,必须纳入模型发布治理。
常见误区
误区一:LoRA 很小,所以不用管理
LoRA 单个文件小,不代表几百个适配器同时在线也小。显存不是只给 LoRA 用,还要给 KV Cache、batch buffer、通信 buffer 和框架 runtime 留空间。
误区二:只监控 GPU 利用率就够了
Multi-LoRA 的问题经常表现为 TTFT 抖动、adapter load latency、cache miss、队头阻塞,而不是 GPU 利用率下降。只看 GPU 利用率很容易漏掉真实瓶颈。
误区三:动态加载等于生产弹性
动态加载只是能力,不是治理。没有注册表、权限、签名、灰度和回滚,动态加载会变成线上风险入口。
误区四:所有租户使用同一套缓存策略
不同租户的 SLO、成本预算和调用规律不同。高价值低延迟租户应有更强缓存保障,低频租户可以接受 cold start。
上线检查清单
- 是否定义
adapter_id命名规范和版本规范; - 是否记录
base_model与 adapter 的兼容关系; - 是否限制最大 Rank、最大适配器大小和最大并发;
- 是否有 Hot / Warm / Cold 分层策略;
- 是否有缓存命中率、加载延迟、驱逐次数、显存占用指标;
- 是否支持请求级 cost attribution(成本归因);
- 是否支持 adapter 级灰度发布和快速回滚;
- 是否禁止用户传入任意 LoRA 路径;
- 是否对适配器文件做哈希校验和签名校验;
- 是否在压测中覆盖冷启动、热点突增、批量发布、回滚和租户隔离场景。
参考资料
- vLLM LoRA Adapters 文档
- S-LoRA: Serving Thousands of Concurrent LoRA Adapters — arXiv:2311.03285
- Punica: Multi-Tenant LoRA Serving — arXiv:2310.18547
- Chameleon: Adaptive Caching and Scheduling for Many-Adapter LLM Inference Environments — arXiv:2411.17741
- CaraServe: CPU-Assisted and Rank-Aware LoRA Serving for Generative LLM Inference — arXiv:2401.11240
- LoRAServe: Serving Heterogeneous LoRA Adapters in Distributed LLM Inference Systems — arXiv:2511.22880