SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 更新于 2026-08-22

Ollama 并发怎么配置:NUM_PARALLEL 与 MAX_QUEUE

Ollama 第二个请求会等待还是收到 HTTP 503?本文解释 OLLAMA_NUM_PARALLEL 和 OLLAMA_MAX_QUEUE 的作用,并说明每增加一个并发槽位为何会按比例占用 VRAM。

第二个 Ollama 请求在第一个请求生成期间会发生什么

Ollama 的并发行为由 3 个环境变量决定。默认情况下,一个已加载的模型一次只处理一个请求。第二个请求不会被拒绝,也不会收到部分答案。它会在队列中等待,直到有可用的并发槽位,然后以正常速度运行。

传入请求有 3 种可能结果。它可以立即在空闲槽位中开始运行,也可以在队列中等待。如果队列已满,服务器会通过 HTTP 503 拒绝请求。具体结果由 OLLAMA_NUM_PARALLEL、OLLAMA_MAX_QUEUE 和 OLLAMA_MAX_LOADED_MODELS 决定。

默认设置较为安全。这也是在系统没有故障时,第二个用户仍可能认为服务器“卡住”的原因。增加并发槽位只需修改两行配置。真正需要注意的是内存。每个并发槽位都需要独立的键值缓存(KV cache)。这是模型用于保存已处理令牌的内存区域。如果在不增加 VRAM(GPU 上的视频内存)的情况下增加并发槽位,就会使原本缓慢的响应变成加载失败。

OLLAMA_NUM_PARALLEL、OLLAMA_MAX_LOADED_MODELS 和 OLLAMA_MAX_QUEUE 分别控制什么

以下是截至 2026 年 8 月当前 Ollama 版本中的默认值。请通过下文所示的日志行检查您自己的设置,不要直接采用这里的数值。

  • OLLAMA_NUM_PARALLEL 表示一个已加载模型同时处理的请求数。默认值为 1,因此请求会依次处理。
  • OLLAMA_MAX_LOADED_MODELS 表示同时常驻的不同模型数。默认值为 0,表示由 Ollama 自动选择:每个 GPU 使用 3 个模型;没有 GPU 的机器使用 3 个模型。
  • OLLAMA_MAX_QUEUE 表示可以处于等待状态的请求数。默认值为 512。队列已满时到达的请求会立即被拒绝。

最坏情况下的内存占用,等于前两个参数的乘积。两个已加载模型各有 4 个槽位时,KV cache 会分配 8 个槽位,并同时常驻内存;Ollama 会尝试满足这一需求。对于单 GPU 服务器,通常更适合只保留一个模型并为其分配多个槽位,因为这样更容易直接计算。

为什么每个并行槽位都会占用 VRAM

Ollama 加载模型时会启动一个独立的 runner 进程。它传递的两个参数与此有关:-c 是 runner 为 KV cache 分配的总上下文长度,-np 是并行序列的数量。Ollama 会将 -c 设置为单个请求的上下文长度乘以槽位数量。然后,runner 将这个总长度平均分配给各个槽位,因此每个请求仍会获得您指定的上下文长度。

这就是全部限制,也是并行处理并非没有成本的原因。在单个请求上下文长度不变的情况下,将槽位从 1 增加到 4,所需的 KV cache 会变为 4 倍。各槽位之间不会共享 KV cache;空闲槽位占用的部分也不会借给繁忙槽位,因为这个划分在 runner 启动时就已固定。

您可以读取实际使用的数值,而不是自己设置的数值:

journalctl -u ollama --no-pager -n 500 | grep "starting llama-server"

该行包含完整的 runner 命令行,其中包括 -c 和 -np。如果设置变量后 -np 为 1,说明该设置没有传递到服务器,下一节将介绍原因。

如果模型权重和 KV cache 的总量无法装入 VRAM,Ollama 会将部分层移至系统 RAM,并由 CPU 运行这些层。CPU 层的速度远低于 GPU 层,因此所有请求都会变慢,包括您最初启动的单个请求。因此,提高并行度可能降低吞吐量,而不是提高吞吐量。对于足够大的模型,仅模型权重就会在进行槽位计算前决定结果。这也是为什么 自托管 Kimi K3 这类规模的模型 讨论的是您有多少张显卡,而不是设置了多少个槽位。

ollama ps

当整体都能装入 VRAM 时,PROCESSOR 列会显示 100% GPU。类似 35%/65% CPU/GPU 的拆分表示模型的一部分正在 CPU 上运行。SIZE 列包括 KV cache,因此增加槽位数量并重新加载模型后,该列的数值会增大。提高 OLLAMA_NUM_PARALLEL,重启,发送一个请求,然后再次运行 ollama ps:这样可以测量更改带来的内存开销,而不是进行估算。如果测量结果表明模型已无法装入 VRAM,请记住模型权重也占用同一份预算;将模型从 fp16 切换为 q8 或 q4 构建 通常比增加一个槽位释放更多 VRAM。

上下文长度和槽位数量会相乘,因此必须一起选择。4 个槽位配合较大的上下文,就是 4 个较大的上下文。如果您还在调整 模型的 num_ctx 上下文窗口,请一次只修改其中一个参数,否则无法判断是哪一个参数占满了显卡。

如何设置这些变量以便重启后仍然生效

在 Linux 上,Ollama 作为 systemd 服务运行。在 shell 中执行 export OLLAMA_NUM_PARALLEL=4 不会产生任何影响,因为 systemd 使用自己的环境启动服务,无法看到你的 shell 环境。请使用 drop-in 文件。

sudo systemctl edit ollama.service

在打开的编辑器中添加以下内容:

[Service]
Environment="OLLAMA_NUM_PARALLEL=4"
Environment="OLLAMA_MAX_LOADED_MODELS=1"
Environment="OLLAMA_MAX_QUEUE=32"

然后重新加载并重启服务:

sudo systemctl daemon-reload
sudo systemctl restart ollama
systemctl show ollama --property=Environment

systemctl show 会输出 systemd 将传递给进程的内容。如果其中缺少你的变量,说明 drop-in 文件未保存,或跳过了 daemon-reload。同时从服务器自身确认:

journalctl -u ollama --no-pager | grep "server config" | tail -1

Ollama 启动时会在一条消息为 server config 的日志中记录完整环境变量。该映射就是实际生效的内容。这是确认变量是否生效的最快方法。

已经加载的模型会继续使用启动时设置的槽位数量,因为该值会在 runner 进程启动时固定下来。上面的重启操作会卸载所有内容,因此下一次请求会使用新设置重新加载模型,并只承担一次加载时间。此后模型在内存中保留多长时间由另一项设置控制,详见让 Ollama 模型在请求之间保持加载状态。

从客户端观察已处理、排队和拒绝请求

同时发送多个请求并测量耗时。以下命令会并行运行 8 个流式请求,并输出每个请求的状态和耗时:

for i in $(seq 1 8); do
  curl -s -o /dev/null \
    -w "req$i http=%{http_code} ttfb=%{time_starttransfer}s total=%{time_total}s\n" \
    http://127.0.0.1:11434/api/generate \
    -d '{"model":"llama3.2:3b","prompt":"Explain what a KV cache is.","stream":true}' &
done
wait

ttfb 是流的首字节时间。由于第一个流式分块包含第一个 token,因此它接近首 token 时间(TTFT)。

并行处理。 每个请求的 ttfb 相近,所有请求的 total 会同时增加。运行中的槽位共享 GPU,因此每个响应都比单独运行时更慢,但每分钟完成的响应更多。这就是提高 OLLAMA_NUM_PARALLEL 后所获得的运行模式。

排队。 前几个请求会快速返回,后面的请求则显示较大的 ttfb,随后以正常速度生成内容。等待时间来自队列,而不是模型。用户在聊天窗口中会看到较长的空白等待,然后文本以全速出现。这种先启动缓慢、随后生成迅速的形态,是排队而不是 GPU 过载的特征。

拒绝。 客户端几乎会立即收到 http=503,响应正文为:

{"error":"server busy, please try again.  maximum pending requests exceeded"}

这条消息表示请求到达时队列已满。它不能说明 VRAM 的使用情况,也不能说明模型的状态。

需要明确的一点是:Ollama 不会公开队列深度。ollama ps 和 /api/ps 端点报告的是已加载的模型,而不是正在等待的请求。因此,应从客户端侧测量队列,例如监控首字节时间;也可以统计前置组件收到的 503 响应数量。

为什么较小的 MAX_QUEUE 通常是更好的设置

512 的队列看起来很充裕,但在单个 slot 上几乎没有用。第 300 个请求要排在前面 299 个完整生成任务之后。这至少需要几分钟。所有 HTTP 客户端都会在此之前放弃,因此调用方只会看到客户端超时。这个信息无法说明原因,也无法让监控系统针对原因发出告警。

将队列设置为服务器能在客户端超时时间内处理的请求量左右。这样,队列溢出时会立即返回 503。503 很有用:反向代理可以重试,客户端可以退避,仪表板可以统计,人员也能直接读懂。根据你自己的测量结果计算这个数值。如果一次生成大约需要 10 秒,而客户端等待 60 秒,那么每个 slot 在该时间窗口内大约可以处理 6 个请求。超过这个深度的队列只会产生超时。

何时应在 Ollama 前加入队列

内置队列采用先进先出(FIFO)策略,并不知道请求来自哪个调用方。对于一个应用连接一台服务器的场景,这已经足够;额外引入基础设施只会增加故障模式。出现以下任一情况时,应考虑在前面加入其他组件。

  • 需要优先级。交互式聊天不应排在批量摘要任务之后等待。Ollama 队列不支持优先级,因此必须在外部暂存批处理任务,并逐步提交。
  • 需要公平性。单个客户端可以独自填满队列,其他客户端随后都会收到 503。
  • 需要让任务在重启后继续存在。队列保存在服务器内存中。重启 Ollama 后,所有等待中的请求都会丢失。
  • 需要真正的重试和退避,并将相关记录保存到重启后仍可检查的位置。

轻量方案是反向代理。在 nginx 中,limit_conn限制并发连接数,limit_req限制每个客户端的请求到达速率。这样,超出限制的请求会在代理处被拒绝,不会进入 Ollama 队列。重量级方案是在 worker 前面加入带数据库的作业队列,由 worker 调用 Ollama。当请求必须跨越进程重启继续存在时,应采用这种方案。针对实际流量确定容量需要单独计算:规划支持并发用户的自托管 LLM介绍了相关计算方法,在 VPS 上运行 Ollama介绍了这些变量所依赖的基础安装。

何时应选择其他服务器

有一个上限,无法通过调整参数突破。模型加载时,Ollama 会将 KV 缓存划分为大小相同的固定槽位。空闲槽位的内存无法供繁忙槽位使用,而且不卸载模型就无法更改槽位数量。这种设计适合单个用户、小型团队或编码代理。

面向大量并发用户的服务器采用不同的方式。它们会按需以小页面分配 KV 缓存,并将新到达的请求加入已在运行的批处理中。因此,内存使用量会随实际需求变化,而不是按固定比例划分。如果目标是在一块 GPU 上支持大量并发用户,这种架构差异比 OLLAMA_NUM_PARALLEL 的任何取值都更重要。Ollama 与 vLLM 的比较正是进行这项判断的依据。不过,不要仅凭原则切换:其他服务器的运维工作更多;如果流量只有少数几个人,内置行为才是正确选择。

测量实际吞吐量和首个 token 延迟

公开的每秒生成 token 数来自其他人的 GPU、模型、量化设置和上下文长度。这些条件都不一定与你的环境相同。因此,阅读到的任何数值都只能作为粗略参考,应直接测量当前机器的性能。

Ollama 会在每次响应的最终 JSON 对象中返回计时信息。eval_count 表示生成的 token 数量,eval_duration 表示生成这些 token 所用的时间,单位为纳秒。

sudo apt install -y jq
curl -s http://127.0.0.1:11434/api/generate \
  -d '{"model":"llama3.2:3b","prompt":"Explain what a KV cache is.","stream":false}' \
  | jq '{prompt_eval_count, eval_count, eval_duration, tokens_per_second: (.eval_count / (.eval_duration / 1000000000))}'

先使用一个 slot 运行一次,再使用你实际预计的并发数运行一次,然后比较两个决定用户体验的指标:首个 token 延迟,以及每个请求的每秒 token 数。增加 slot 后,单个请求的吞吐量总会下降。需要判断的是,下降幅度是否超过用户可以接受的范围。测量本地 LLM 的每秒 token 数更详细地介绍了具体方法,包括如何在多次运行之间保持 prompt 不变。

宽松队列的公网端点会成为拒绝服务攻击目标

设置 OLLAMA_HOST=0.0.0.0:11434 会让 API 监听所有网络接口,而 Ollama 没有内置身份验证。使用默认队列的开放端点会接受来自任何发现它的人的 512 个等待请求。攻击者几乎不需要付出成本就能填满队列:提示词可以很长,不需要登录,没有速率限制,也不会产生费用。此时,您自己的用户会收到 503 响应或长时间等待,而机器会一直处于繁忙状态。

让监听器继续绑定 loopback,通过 SSH 隧道或私有网络访问;或者在端点前配置身份验证和速率限制。保护 Ollama API 端点涵盖了这两种方式。完成这些配置后再调整队列,因为队列长度是容量设置,不能提供任何保护。

FAQ

为什么我的第二个 Ollama 请求要等第一个请求完成?

因为 OLLAMA_NUM_PARALLEL 默认为 1,因此已加载的模型一次只处理一个请求,其余请求按顺序等待。等待中的请求会保持 HTTP 连接打开,但在有空闲槽位前不会发送任何字节。从客户端看,这与模型响应缓慢完全相同。判断方法是观察时序:长时间暂停后文本立即以全速输出,说明请求在排队;从第一个 token 开始就持续缓慢输出,才说明模型运行缓慢。通过 systemd drop-in 提高槽位数,然后重启服务。

“server busy, please try again. maximum pending requests exceeded”是什么意思?

这是 Ollama 的队列溢出错误,返回的 HTTP 状态为 503。当前等待中的请求数已达到 OLLAMA_MAX_QUEUE。该值默认为 512,因此最新请求被拒绝,没有加入队列。这不是内存错误,也不是模型错误。增大队列只会让调用方等待更久,之后仍然收到相同的拒绝。因此,真正的解决方法是:在 VRAM 足够的情况下增加槽位、降低传入负载,或在前面设置一个能够重试和进行优先级调度的队列。

增大 OLLAMA_NUM_PARALLEL 会让 Ollama 更快吗?

不会。它允许同时处理更多请求,但每个请求都会比单独运行时更慢,因为这些请求共享同一个 GPU。它还会使 KV cache 按比例增加,因为 Ollama 启动 runner 时使用的总上下文大小为上下文长度乘以槽位数。如果结果无法全部放入 VRAM,Ollama 会将部分层转移到 CPU,所有请求都会变慢,包括没有竞争的单个请求。修改后检查 ollama ps,确认其中的 PROCESSOR 列仍显示 100% GPU。

修改这些变量后需要重启 Ollama 吗?

需要。服务器会在启动时读取这些变量,正在运行的模型会继续使用其 runner 进程启动时确定的槽位数。使用 sudo systemctl edit ollama.service 编辑 drop-in,然后运行 sudo systemctl daemon-reload 和 sudo systemctl restart ollama。使用 systemctl show ollama --property=Environment 确认,再检查 journalctl -u ollama 中的 server config 行;该行列出服务器实际加载的环境变量。

应设置多少个并行槽位?

从 1 开始,每次增加一个槽位。每次修改后重启 Ollama,发送一个请求加载模型,然后运行 ollama ps。当 PROCESSOR 仍显示 100% GPU,且 SIZE 列为你提供的最长上下文保留足够余量时,将该值作为上限。随后在真实并发条件下,使用该设置测量首 token 时间和每秒 token 数。如果单个请求的速度低于用户可接受的水平,则退回一个槽位。

#ollama#concurrency#vram#queueing#self-hosted-llm