SSD Nodes Learn 🎉 VPS od $5.50/mies.
Przewodniki Matt ConnorAutor: Matt Connor

Ollama: jak działa OLLAMA_NUM_PARALLEL i OLLAMA_MAX_QUEUE

Zrozum mechanizm kolejkowania żądań w Ollama. Dowiedz się, kiedy serwer zwraca błąd HTTP 503 oraz jak zwiększenie liczby slotów wpływa na zużycie pamięci VRAM i wydajność modelu.

Co dzieje się z drugim żądaniem do Ollama w trakcie generowania odpowiedzi na pierwsze

Współbieżność w Ollama jest określana przez trzy zmienne środowiskowe, a domyślnie załadowany model obsługuje jedno żądanie w danym momencie. Drugie żądanie nie jest odrzucane i nie otrzymuje częściowej odpowiedzi. Oczekuje ono w kolejce, aż zwolni się miejsce, po czym jest przetwarzane z normalną prędkością.

Przychodzące żądanie może zostać obsłużone na trzy sposoby. Rozpoczyna się natychmiast, jeśli dostępny jest wolny slot. Oczekuje w kolejce. Lub, jeśli kolejka jest pełna, serwer odrzuca je z błędem HTTP 503. O tym, który scenariusz wystąpi, decydują zmienne OLLAMA_NUM_PARALLEL, OLLAMA_MAX_QUEUE oraz OLLAMA_MAX_LOADED_MODELS.

Ustawienia domyślne są bezpieczne, co wyjaśnia, dlaczego drugi użytkownik może zgłaszać, że serwer "zawiesił się", mimo że wszystko działa poprawnie. Dodanie slotów wymaga zmiany dwóch linii w konfiguracji. Problematycznym aspektem jest pamięć. Każdy równoległy slot wymaga własnej pamięci podręcznej kluczy/wartości (KV cache), czyli bloku pamięci, w którym model przechowuje przetworzone już tokeny. Dodanie slotów bez zwiększenia ilości VRAM (pamięci wideo na GPU) spowoduje, że zamiast wolnej odpowiedzi, wystąpi błąd ładowania modelu.

Co kontrolują parametry OLLAMA_NUM_PARALLEL, OLLAMA_MAX_QUEUE oraz OLLAMA_MAX_LOADED_MODELS

Poniżej przedstawiono wartości domyślne w aktualnych wydaniach Ollama na sierpień 2026 roku. Zamiast polegać na poniższych liczbach, należy zweryfikować własną konfigurację, korzystając z wiersza logu przedstawionego w dalszej części.

  • OLLAMA_NUM_PARALLEL określa, ile żądań jednocześnie obsługuje jeden załadowany model. Wartość domyślna to 1, co oznacza, że żądania są przetwarzane sekwencyjnie.
  • OLLAMA_MAX_LOADED_MODELS określa, ile różnych modeli pozostaje jednocześnie w pamięci. Wartość domyślna to 0, co oznacza, że Ollama dokonuje wyboru automatycznie: trzy modele na GPU oraz trzy na maszynie bez GPU.
  • OLLAMA_MAX_QUEUE określa, ile żądań może oczekiwać w kolejce. Wartość domyślna to 512. Żądanie, które nadejdzie w momencie, gdy kolejka jest pełna, zostaje natychmiast odrzucone.

Największe zapotrzebowanie na pamięć jest iloczynem pierwszych dwóch parametrów. Dwa załadowane modele z czterema slotami każdy oznaczają osiem alokacji slotów pamięci podręcznej KV, które muszą być jednocześnie aktywne, a Ollama podejmie próbę ich obsłużenia. Na maszynie z pojedynczym GPU zazwyczaj lepiej jest utrzymywać jeden model i przydzielić mu więcej slotów, ponieważ obliczenia pozostają wtedy na tyle proste, że można je wykonać w pamięci.

Dlaczego każdy równoległy slot zużywa VRAM

Gdy Ollama ładuje model, uruchamia osobny proces wykonawczy. Istotne są tutaj dwa przekazywane argumenty: -c to całkowity kontekst, dla którego proces wykonawczy alokuje pamięć podręczną KV, a -np to liczba równoległych sekwencji. Ollama ustawia -c jako długość kontekstu na żądanie pomnożoną przez liczbę slotów. Proces wykonawczy dzieli następnie tę sumę równo między sloty, dzięki czemu każde żądanie otrzymuje zadeklarowaną długość kontekstu.

To całe ograniczenie i powód, dla którego równoległość nie jest darmowa. Przejście z jednego slotu na cztery wymaga czterokrotnie większej pamięci podręcznej KV przy tej samej długości kontekstu na żądanie. Między slotami nie jest współdzielone nic, a przydział bezczynnego slotu nie jest przekazywany do zajętego, ponieważ podział jest ustalany w momencie uruchomienia procesu.

Można odczytać rzeczywiste wartości zamiast tych, które zamierzano ustawić:

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

Ta linia zawiera pełne polecenie uruchomienia procesu, w tym -c oraz -np. Jeśli -np wynosi 1 po ustawieniu zmiennej, ustawienie nie dociera do serwera, co wyjaśnia kolejna sekcja.

Jeśli wagi modelu wraz z pamięcią podręczną KV nie mieszczą się w VRAM, Ollama przenosi część warstw do pamięci RAM systemu i warstwy te są przetwarzane przez CPU. Warstwy CPU są znacznie wolniejsze od warstw GPU, co spowalnia każde żądanie, w tym pierwsze, od którego rozpoczęto pracę. Zwiększenie równoległości może zatem obniżyć przepustowość zamiast ją zwiększyć.

ollama ps

Kolumna PROCESSOR wskazuje 100% GPU, gdy całość mieści się w pamięci. Podział typu 35%/65% CPU/GPU oznacza, że część modelu działa na CPU. Kolumna SIZE uwzględnia pamięć podręczną KV, więc rośnie ona wraz ze zwiększeniem liczby slotów i przeładowaniem modelu. Zwiększ OLLAMA_NUM_PARALLEL, zrestartuj usługę, wyślij jedno żądanie i uruchom ponownie ollama ps: to rzeczywisty koszt pamięciowy zmiany, zmierzony, a nie oszacowany.

Długość kontekstu i liczba slotów mnożą się, więc muszą być dobierane łącznie. Duży kontekst przy czterech slotach oznacza cztery duże konteksty. W przypadku dostrajania okna kontekstowego num_ctx dla modelu, należy zmieniać tylko jeden z tych parametrów naraz, w przeciwnym razie nie będzie wiadomo, który z nich zapełnił kartę graficzną.

Jak skonfigurować te zmienne, aby przetrwały restart systemu

W systemie Linux Ollama działa jako usługa systemd. Uruchomienie export OLLAMA_NUM_PARALLEL=4 w powłoce nie przynosi efektu, ponieważ systemd uruchamia usługę w odrębnym środowisku, które nie dziedziczy zmiennych powłoki użytkownika. Należy użyć pliku typu drop-in.

sudo systemctl edit ollama.service

Dodaj poniższą treść w edytorze, który zostanie otwarty:

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

Następnie przeładuj konfigurację i zrestartuj usługę:

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

systemctl show wyświetla zmienne, które systemd przekaże do procesu. Jeśli zmienna nie jest widoczna, oznacza to, że plik drop-in nie został zapisany lub pominięto daemon-reload. Weryfikację należy przeprowadzić również od strony serwera:

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

Ollama loguje pełne środowisko podczas uruchamiania w linii zawierającej komunikat server config. Ta mapa jest wiarygodnym źródłem informacji. Jest to najszybszy sposób na sprawdzenie, czy zmienna została poprawnie zastosowana.

Model, który został już załadowany, zachowuje liczbę slotów zdefiniowaną w momencie uruchomienia, ponieważ wartość ta jest przypisana do procesu wykonawczego przy starcie. Powyższy restart powoduje zwolnienie wszystkich zasobów, dzięki czemu kolejne żądanie załaduje model z nowymi ustawieniami, co wiąże się z jednorazowym czasem oczekiwania na załadowanie. Czas utrzymywania modelu w pamięci po tym procesie jest kontrolowany oddzielnie, co opisano w utrzymywanie modelu Ollama w pamięci między żądaniami.

Jak wyglądają stany obsłużonych, zakolejkowanych i odrzuconych żądań z perspektywy klienta

Wyślij kilka żądań jednocześnie i zmierz czas ich wykonania. Poniższe polecenie uruchamia osiem równoległych żądań strumieniowych i wyświetla status oraz czas dla każdego z nich:

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 to czas do otrzymania pierwszego bajtu strumienia, który jest zbliżony do czasu do pierwszego tokena (TTFT), ponieważ pierwszy przesłany fragment zawiera pierwszy token.

Obsługa równoległa. Każde żądanie raportuje podobny czas ttfb, a wartość total rośnie dla wszystkich jednocześnie. GPU jest współdzielone między aktywne sloty, więc każda odpowiedź jest wolniejsza niż w przypadku pojedynczego żądania, ale w ciągu minuty kończy się ich więcej. Jest to tryb pracy, który zyskujesz po zwiększeniu OLLAMA_NUM_PARALLEL.

Kolejkowanie. Pierwsze żądania odpowiadają szybko, a kolejne wykazują dużą wartość ttfb, po której następuje normalne generowanie tekstu. Czas oczekiwania wynika z kolejki, a nie z pracy modelu. Użytkownik obserwujący okno czatu widzi długą pauzę, a następnie tekst generowany z pełną prędkością. Taki schemat – powolny start i szybkie generowanie – jest sygnaturą kolejki, a nie przeciążonego GPU.

Odrzucenie. Klient otrzymuje http=503 niemal natychmiast, a treść odpowiedzi wygląda następująco:

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

Ten komunikat oznacza, że kolejka była pełna w momencie nadejścia żądania. Nie informuje on o stanie VRAM ani o modelu.

Jedno uczciwe ograniczenie: Ollama nie publikuje głębokości kolejki. ollama ps oraz endpoint /api/ps raportują załadowane modele, a nie oczekujące żądania. Dlatego kolejkę należy mierzyć od strony klienta, obserwując czas do pierwszego bajtu, lub zliczać odpowiedzi 503 na poziomie komponentu znajdującego się przed usługą.

Dlaczego mniejsza wartość MAX_QUEUE jest często lepszym ustawieniem

Kolejka o rozmiarze 512 brzmi na dużą, lecz w przypadku pojedynczego slotu jest niemal bezużyteczna. Żądanie numer 300 czeka na zakończenie 299 poprzednich generacji. W najlepszym przypadku zajmuje to minuty. Każdy klient HTTP zrezygnuje znacznie wcześniej, więc wywołujący otrzyma błąd przekroczenia czasu po stronie klienta, który nie informuje o przyczynie i nie pozwala systemom monitoringu na wygenerowanie alertu.

Ustaw kolejkę na wartość zbliżoną do liczby zadań, które serwer jest w stanie przetworzyć w czasie oczekiwania klienta. Dzięki temu przepełnienie skutkuje natychmiastowym błędem 503. Kod 503 jest użyteczny: reverse proxy może ponowić próbę, klient może zastosować mechanizm backoff, panel monitoringu może zliczyć błąd, a administrator może go odczytać. Wyznacz tę liczbę na podstawie własnych pomiarów. Jeśli generacja trwa około dziesięciu sekund, a klient czeka sześćdziesiąt, to w tym oknie czasowym można przetworzyć około sześciu żądań na slot. Kolejka głębsza niż ta wartość generuje jedynie przekroczenia czasu.

Kiedy umieścić kolejkę przed Ollama

Wbudowana kolejka działa w trybie FIFO (pierwsze weszło, pierwsze wyszło) i nie posiada informacji o tożsamości klienta. W przypadku jednej aplikacji komunikującej się z jednym serwerem jest to wystarczające, a dodawanie infrastruktury zwiększyłoby jedynie liczbę możliwych punktów awarii. Rozważ zastosowanie zewnętrznego rozwiązania, gdy spełniony jest którykolwiek z poniższych warunków:

  • Wymagana jest priorytetyzacja. Interaktywny czat nie powinien czekać w kolejce za zadaniem przetwarzania wsadowego. Kolejka Ollama nie obsługuje priorytetów, więc zadania wsadowe muszą być wstrzymywane na zewnątrz i przekazywane stopniowo.
  • Wymagana jest sprawiedliwość dostępu. Jeden klient może samodzielnie zapełnić kolejkę, co spowoduje zwracanie błędu 503 wszystkim pozostałym użytkownikom.
  • Zadania muszą przetrwać restart usługi. Kolejka znajduje się w pamięci operacyjnej serwera. Restart Ollama powoduje utratę wszystkich oczekujących żądań.
  • Wymagane są rzeczywiste mechanizmy ponawiania prób z wycofywaniem wykładniczym (backoff), rejestrowane w miejscu dostępnym do późniejszej analizy.

Lżejszym rozwiązaniem jest użycie reverse proxy. W nginx, limit_conn ogranicza liczbę jednoczesnych połączeń, a limit_req limituje częstotliwość żądań na klienta, dzięki czemu nadmiarowy ruch jest odrzucany na poziomie proxy i nigdy nie trafia do kolejki Ollama. Rozwiązaniem bardziej zaawansowanym jest kolejka zadań z bazą danych, umieszczona przed workerem wywołującym Ollama; jest to podejście zalecane, gdy żądania muszą przetrwać restart procesu. Skalowanie takiego rozwiązania pod rzeczywisty ruch jest osobnym zagadnieniem: planowanie własnej instancji LLM dla wielu użytkowników zawiera niezbędne obliczenia, a uruchamianie Ollama na VPS opisuje podstawową instalację, na której opierają się te zmienne.

Kiedy uczciwą odpowiedzią jest inny serwer

Istnieje limit, którego nie da się obejść poprzez dostrajanie. Ollama dzieli pamięć podręczną KV na równe, stałe sloty w momencie ładowania modelu. Pamięć bezczynnego slotu nie może zostać wykorzystana przez slot zajęty, a liczba slotów nie może ulec zmianie bez wyładowania modelu. Taka architektura sprawdza się w przypadku jednej osoby, małego zespołu lub agenta programistycznego.

Serwery zaprojektowane do obsługi wielu jednoczesnych użytkowników działają inaczej. Przydzielają one pamięć podręczną KV w małych stronach na żądanie i dodają przychodzące zapytania do już działającej partii, dzięki czemu zużycie pamięci podąża za rzeczywistym zapotrzebowaniem, a nie za sztywnym podziałem. Jeśli celem jest obsługa dużej liczby współbieżnych użytkowników na jednym GPU, ta różnica architektoniczna ma większe znaczenie niż jakakolwiek wartość OLLAMA_NUM_PARALLEL. Porównanie Ollama i vLLM to miejsce, w którym należy podjąć tę decyzję. Nie należy jednak zmieniać rozwiązania z czystej zasady: inny serwer oznacza więcej pracy przy utrzymaniu, a jeśli ruch generuje tylko kilka osób, wbudowane zachowanie jest właściwym wyborem.

Pomiar własnej przepustowości i czasu do pierwszego tokena

Wartości tokenów na sekundę publikowane w sieci pochodzą z cudzych jednostek GPU, modeli, kwantyzacji, długości kontekstu oraz promptów. Żaden z tych parametrów nie odpowiada Twojej konfiguracji, dlatego każdą napotkaną liczbę traktuj jedynie jako przybliżoną wskazówkę i dokonaj pomiaru na własnym sprzęcie.

Ollama zwraca czasy w końcowym obiekcie JSON każdej odpowiedzi. eval_count to liczba wygenerowanych tokenów, a eval_duration to czas poświęcony na ich generowanie, wyrażony w nanosekundach.

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))}'

Uruchom test z jednym slotem, a następnie powtórz go przy oczekiwanym poziomie współbieżności. Porównaj dwie wartości decydujące o zadowoleniu użytkowników: czas do pierwszego tokena oraz liczbę tokenów na sekundę dla każdego zapytania. Przepustowość na zapytanie zawsze spada wraz ze zwiększaniem liczby slotów. Kluczowe jest sprawdzenie, czy spadek ten nie przekracza akceptowalnego dla użytkowników poziomu. Pomiar liczby tokenów na sekundę w lokalnym modelu LLM szczegółowo opisuje tę metodę, w tym sposób utrzymania stałego promptu pomiędzy uruchomieniami.

Publiczny punkt końcowy z dużą kolejką jest celem ataku typu denial of service

Ustawienie OLLAMA_HOST=0.0.0.0:11434 udostępnia API na wszystkich interfejsach, a Ollama nie posiada wbudowanego mechanizmu uwierzytelniania. Otwarty punkt końcowy z domyślną kolejką zaakceptuje 512 oczekujących żądań od każdego, kto go odnajdzie. Wypełnienie tej kolejki nic nie kosztuje atakującego: wystarczą długie zapytania, brak logowania, brak limitów prędkości i brak opłat. Własni użytkownicy otrzymują wtedy odpowiedzi 503 lub długo czekają, a maszyna jest stale obciążona.

Pozostaw nasłuchiwanie na interfejsie loopback i uzyskuj dostęp przez tunel SSH lub sieć prywatną, albo umieść przed usługą warstwę uwierzytelniania i limitowania zapytań. Zabezpieczanie punktu końcowego Ollama API omawia oba te rozwiązania. Dopiero po ich wdrożeniu dostosuj kolejkę, ponieważ długość kolejki jest parametrem wydajnościowym i nie zapewnia żadnej ochrony.

FAQ

Dlaczego drugie żądanie do Ollama czeka na zakończenie pierwszego?

Ponieważ OLLAMA_NUM_PARALLEL domyślnie wynosi 1, więc załadowany model przetwarza jedno żądanie na raz, a pozostałe oczekują w kolejce. Oczekujące żądanie utrzymuje otwarte połączenie HTTP i nie przesyła żadnych danych, dopóki nie zwolni się slot, co z perspektywy klienta wygląda jak powolne działanie modelu. Wskazówką jest charakterystyka czasowa: długa pauza, po której następuje tekst z pełną prędkością, oznacza kolejkę, natomiast powolne generowanie od pierwszego tokena oznacza powolny model. Zwiększ liczbę slotów za pomocą pliku typu drop-in dla systemd i zrestartuj usługę.

Co oznacza komunikat "server busy, please try again. maximum pending requests exceeded"?

Jest to błąd przepełnienia kolejki Ollama, zwracany z kodem statusu HTTP 503. Liczba oczekujących żądań osiągnęła wartość OLLAMA_MAX_QUEUE, która domyślnie wynosi 512, więc najnowsze żądanie zostało odrzucone zamiast dodane do kolejki. Nie jest to błąd pamięci ani błąd modelu. Zwiększenie kolejki powoduje jedynie, że klienci czekają dłużej przed otrzymaniem tego samego odrzucenia, dlatego rzeczywistym rozwiązaniem jest zwiększenie liczby slotów (jeśli dysponujesz odpowiednią ilością VRAM), zmniejszenie obciążenia przychodzącego lub zastosowanie kolejki przed serwerem, która potrafi ponawiać żądania i nadawać im priorytety.

Czy zwiększenie OLLAMA_NUM_PARALLEL przyspiesza Ollama?

Nie. Pozwala to na jednoczesne uruchomienie większej liczby żądań, z których każde jest wolniejsze niż w przypadku pracy w pojedynkę, ponieważ współdzielą one jeden GPU. Zwiększa to również zużycie pamięci przez KV cache, ponieważ Ollama uruchamia proces wykonawczy z całkowitym kontekstem równym długości kontekstu pomnożonej przez liczbę slotów. Jeśli wynik przestanie mieścić się w VRAM, Ollama przeniesie warstwy do CPU, co spowolni każde żądanie, nawet jeśli będzie ono jedynym uruchomionym. Sprawdź ollama ps po wprowadzeniu zmiany i upewnij się, że kolumna PROCESSOR nadal wskazuje 100% GPU.

Czy po zmianie tych zmiennych muszę restartować Ollama?

Tak. Serwer odczytuje je podczas uruchamiania, a działający model utrzymuje liczbę slotów ustaloną w procesie wykonawczym w momencie jego startu. Edytuj plik drop-in za pomocą sudo systemctl edit ollama.service, a następnie wykonaj sudo systemctl daemon-reload oraz sudo systemctl restart ollama. Potwierdź zmiany za pomocą systemctl show ollama --property=Environment, a następnie sprawdź linię server config w journalctl -u ollama, która zawiera listę zmiennych środowiskowych faktycznie załadowanych przez serwer.

Ile slotów równoległych należy ustawić?

Zacznij od 1 i zwiększaj wartość stopniowo. Po każdym kroku zrestartuj Ollama, wyślij jedno żądanie w celu załadowania modelu i uruchom ollama ps. Zatrzymaj się na ostatniej wartości, przy której PROCESSOR nadal wskazuje 100% GPU, a kolumna SIZE pozostawia zapas dla najdłuższego obsługiwanego kontekstu. Następnie zmierz czas do pierwszego tokena oraz liczbę tokenów na sekundę przy tym ustawieniu w warunkach rzeczywistego obciążenia i zmniejsz wartość o jeden krok, jeśli prędkość pojedynczego żądania spadła poniżej poziomu akceptowalnego dla użytkowników.

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