OLLAMA_MAX_LOADED_MODELS控制同时加载模型数,OLLAMA_NUM_PARALLEL控制每个模型的并发请求数,OLLAMA_MAX_QUEUE是背压队列,三者相乘决定总并发容量。
Ollama 早就支持同时服务多个模型,所以问题不在于能不能,而在于代价是什么、以及当代价超出显存的会发生什么。这两个问题的答案都比文档看起来的更具体,在把路由器指向单个 Ollama 主机之前都值得了解一下。
两个限制,各司其职
这两个限制经常被混为一谈,但它们控制的是完全不同的东西。Ollama 的 FAQ 对两者都有定义。
OLLAMA_MAX_LOADED_MODELS 是在显存允许的前提下,驻留并发模型的最大数量。文档默认值是 GPU 数量的 3 倍,CPU 推理则为 3。这个限制决定的是"一台主机能否同时服务一个聊天模型和一个嵌入模型"。
OLLAMA_NUM_PARALLEL 是每个模型同时处理的并行请求最大数量,文档默认值是 1。这个限制决定的是"同一模型的两个用户是否需要互相等待"。
OLLAMA_MAX_QUEUE 是兜底机制:当 Ollama 忙碌时最多允许多少请求排队,超出后直接拒绝而非继续等待,默认 512。超过这个数量的请求会被拒绝而不是排队。
重要的是,这几个限制是相乘的关系。三个加载的模型每个有四个并行槽,就是十二个并发序列,而每个槽都拥有自己的 KV cache。在不增加模型数量的情况下提高 OLLAMA_NUM_PARALLEL 来提升吞吐量,是在不触碰任何明显与内存相关设置的情况下把机器显存耗尽的常见方式。
每个驻留模型的代价
一个模型的预算由权重(weights)加上缓存(cache)组成,只有第一部分是固定的。
权重基本等同于文件在磁盘上的大小。量化的 GGUF 文件加载时基本是原样载入的,所以注册列表或模型卡片上的数字就是需要规划的数字。
缓存 = 每 token 字节数 × 上下文长度 × 并行槽数,而每 token 字节数是架构的属性。推导一个模型的缓存——基于 Ollama 的默认上下文长度——足以看清规律:
per model = weights
+ (bytes_per_token x num_ctx x OLLAMA_NUM_PARALLEL)
+ a per-model runtime overhead
total = sum over resident models
+ OLLAMA_GPU_OVERHEAD (whatever you reserved)
+ whatever else is on the card
其中有两个Terms容易被忽略。第一个是桌面本身会占用 VRAM——合成器和浏览器在工作站上可能占用好几 GB,显卡不会把它们吐出来。第二个是 OLLAMA_GPU_OVERHEAD,文档描述为每个 GPU 预留一部分 VRAM(以字节为单位);它存在的正是因为调度器对空闲内存的估计只是一个估计,设置这个值才是防止纸面上能放下但实际加载失败的正确方式。
值得规划的一个不对称性是:小模型便宜得不成比例。嵌入模型通常只有几百 MB 且上下文很短,所以在聊天模型旁边再驻留一个,成本可以忽略不计。另一方面,两个带长上下文的 8B 聊天模型,每个在缓存上的花费可能比一个小模型的总成本还高。
放不下时会发生什么
不会报错。FAQ 直接描述了这个序列:当没有足够内存加载新模型时,所有新请求都会排队,直到新模型可以加载,而随着先前模型变得空闲,一个或多个会被卸载以腾出空间。
注意"空闲"这个词。驱逐不是立即发生的,也不是抢占式的:一个正在生成中的模型不会被踢出去,而只是加载了但当前没在工作的模型才是候选。这个实际结果是延迟尖峰而不是失败。模型 B 的请求到达,模型 A 正忙,B 的请求等待 A 完成,A 被卸载,B 从磁盘加载,只有到那时才开始生成。在大型模型从机械磁盘或网络挂载读取的情况下,这个加载过程需要几秒钟——有时是几十秒。
这就是在两个无法共驻留的模型之间交替是能想到的最差模式的原因。每次请求都要支付一次完整加载,吞吐量崩溃到远低于任一模型独立运行时的速率,而且没有任何错误来解释它。如果你看到双峰延迟分布——大部分请求很快,规律性地有一小部分极慢——模型间颠簸是第一个要怀疑的假设。
OLLAMA_LOAD_TIMEOUT,文档描述为允许模型加载停滞多久才放弃,默认 5 分钟,是最终的兜底。如果你一直在触达这个超时,答案是减少驻留模型数量而不是延长超时时间。
测量实际加载了什么
不要从配置去推算,直接问服务器:
ollama ps
# 或者机器可读的形式
curl -s http://localhost:11434/api/ps
每个条目列出模型名并报告 size、size_vram、context_length 和 expires_at。这四个字段回答的是你真正关心的问题。size 对比 size_vram 告诉你模型是完全在卡上还是部分在系统 RAM 中——如果 size_vram 更小,差值是由 CPU 承载的,这个模型就慢。context_length 告诉你缓存是按什么大小配置的。expires_at 告诉你保活计时器何时会卸载它,这就是区分"模型是驻留的"和"模型即将不再是驻留的"的方式。
观察所有条目的 size_vram 总和与你显卡的总量对比,而不是盯着单个模型。nvidia-smi 显示的卡级别用量是一个有用的交叉验证,但它包含 GPU 上的所有其他东西——这正是为什么要检查它的原因。
可行的配置方案
一个大模型加小型的专用模型。聊天模型、嵌入模型,可能还有一个重排模型共驻留,是默认配置所针对的方案,几乎不需要调优。
把热模型固定住,让其余的自然过期。OLLAMA_KEEP_ALIVE 文档默认值是五分钟,负值则让模型无限期保持加载。把长或无限的 keep_alive 设置给服务交互流量模型,把短的设置给批处理模型,能让你在用户能感知到的地方获得可预测的延迟。参见 keep_alive 的工作方式。
只有在测量完缓存之后再提高并行度。从一个槽变四个,缓存内存会翻四倍。在已经接近上限的机器上,这会把一个正常工作的配置变成颠簸的配置。num_parallel 设置处理的是这其中的吞吐量那一边。
多主机优于聪明的调度。如果两个大模型都需要保持热状态,诚实的答案通常是两台服务器或两张卡,把 OLLAMA_SCHED_SPREAD——文档描述为始终将模型调度到所有 GPU 上——留作一个模型太大单卡放不下时的替代方案,而不是作为同时塞进两个模型的方法。