Ollama: jak działa OLLAMA_NUM_PARALLEL i OLLAMA_MAX_QUEUE
Dowiedz się, jak OLLAMA_NUM_PARALLEL oraz OLLAMA_MAX_QUEUE zarządzają kolejką żądań. Wyjaśniamy, dlaczego zwiększenie slotów powoduje błąd VRAM i jak uniknąć statusu HTTP 503.
Co dzieje się z drugim żądaniem do Ollama, gdy pierwsze jest w trakcie generowania
Współbieżność Ollama jest określana przez trzy zmienne środowiskowe, a domyślnie jeden załadowany model obsługuje jedno żądanie w danym momencie. Drugie żądanie nie jest odrzucane i nie otrzymuje częściowej odpowiedzi. Czeka ono w kolejce, aż zwolni się miejsce, a następnie jest przetwarzane z normalną prędkością.
Przychodzące żądanie może spotkać jeden z trzech scenariuszy. Rozpoczyna się natychmiast w wolnym slocie. Czeka w kolejce. Lub kolejka jest już pełna, a serwer odrzuca żądanie z kodem HTTP 503. O tym, który scenariusz wystąpi, decydują OLLAMA_NUM_PARALLEL, OLLAMA_MAX_QUEUE oraz OLLAMA_MAX_LOADED_MODELS.
Ustawienia domyślne są bezpieczne, co wyjaśnia, dlaczego drugi użytkownik zgłasza, że serwer „zawiesił się”, mimo że wszystko działa poprawnie. Dodanie slotów wymaga zmiany w dwóch liniach. Problemem 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 VRAM (pamięci wideo na GPU) sprawia, że wolna odpowiedź zamienia się w 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 bieżących wydaniach Ollama na sierpień 2026. Zamiast polegać na poniższych liczbach, należy sprawdzić własną konfigurację, korzystając z linii dziennika wskazanej w dalszej części.
OLLAMA_NUM_PARALLELokreśla, ile żądań jednocześnie obsługuje jeden załadowany model. Wartość domyślna to 1, co oznacza, że żądania są przetwarzane kolejno.OLLAMA_MAX_LOADED_MODELSokreś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_QUEUEokreś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.
Najgorszy scenariusz zużycia pamięci to iloczyn pierwszych dwóch wartości. 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. W przypadku pojedynczego GPU zazwyczaj lepiej jest utrzymywać jeden model i przydzielić mu sloty, 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 typu runner. Istotne są tutaj dwa przekazywane argumenty: -c to całkowity kontekst, dla którego runner alokuje pamięć podręczną KV, a -np to liczba równoległych sekwencji. Ollama ustawia -c jako iloczyn długości kontekstu na żądanie oraz liczby slotów. Runner dzieli następnie tę sumę równo pomiędzy sloty, dzięki czemu każde żądanie otrzymuje zadeklarowaną długość kontekstu.
To jedyne 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. Pomiędzy slotami nie ma współdzielenia zasobów, a niewykorzystany slot nie przekazuje swojej części zajętemu, ponieważ podział jest ustalany w momencie uruchomienia runnera.
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 uruchomieniowe runnera, w tym -c oraz -np. Jeśli -np wynosi 1 po ustawieniu zmiennej, ustawienie nie dociera do serwera, a kolejna sekcja wyjaśnia dlaczego.
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, a te warstwy są przetwarzane przez CPU. Warstwy CPU są znacznie wolniejsze niż warstwy GPU, co spowalnia każde żądanie, w tym to pierwsze, od którego rozpoczęto pracę. Zwiększenie równoległości może zatem obniżyć przepustowość zamiast ją zwiększyć. W przypadku wystarczająco dużego modelu same wagi rozstrzygają kwestię jeszcze przed wykonaniem obliczeń dla slotów, dlatego samodzielne hostowanie modelu wielkości Kimi K3 jest rozmową o liczbie posiadanych kart, a nie o liczbie ustawionych slotów.
ollama psKolumna 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 po zwiększeniu liczby slotów i przeładowaniu modelu. Zwiększ OLLAMA_NUM_PARALLEL, zrestartuj usługę, wyślij jedno żądanie i uruchom ponownie ollama ps: to rzeczywisty koszt pamięci wynikający ze zmiany, zmierzony, a nie oszacowany. Jeśli pomiar wykazuje, że model przestał się mieścić, należy pamiętać, że wagi stanowią drugą połowę tego samego budżetu, a przejście z fp16 na build q8 lub q4 często zwalnia więcej VRAM niż kosztuje dodanie kolejnego slotu.
Długość kontekstu i liczba slotów mnożą się, więc muszą być dobierane łącznie. Duży kontekst z czterema slotami to cztery duże konteksty. Jeśli dostrajasz również okno kontekstowe num_ctx dla swojego modelu, zmieniaj tylko jeden z tych parametrów naraz, w przeciwnym razie nie będzie wiadomo, który z nich zapełnił kartę.
Jak ustawić 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 żadnych zmian, ponieważ systemd uruchamia usługę w swoim własnym środowisku i nie ma dostępu do zmiennych powłoki użytkownika. Należy użyć pliku typu drop-in.
sudo systemctl edit ollama.serviceW edytorze, który się otworzy, dodaj następującą treść:
[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=EnvironmentPolecenie systemctl show wyświetla zmienne, które systemd przekaże do procesu. Jeśli zmienna nie pojawia się na liście, oznacza to, że plik drop-in nie został zapisany lub pominięto daemon-reload. Warto również zweryfikować stan po stronie serwera:
journalctl -u ollama --no-pager | grep "server config" | tail -1Ollama rejestruje całe swoje środowisko podczas uruchamiania w linii zawierającej komunikat server config. Ta mapa jest wiarygodnym źródłem informacji. Jest to najszybszy sposób na rozstrzygnięcie, czy zmienna została poprawnie zastosowana.
Model, który jest już załadowany, zachowuje liczbę slotów, z jaką został uruchomiony, ponieważ wartość ta jest przypisana do procesu uruchomieniowego w momencie startu. 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, przez jaki model pozostaje w pamięci po tym zdarzeniu, 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
waitttfb 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łużone równolegle. Każde żądanie raportuje podobny czas ttfb, a wartość total rośnie dla wszystkich jednocześnie. GPU jest współdzielone między aktywnymi slotami, więc każda odpowiedź jest wolniejsza niż w przypadku pracy w pojedynkę, mimo że w ciągu minuty kończy się ich więcej. Jest to tryb pracy uzyskiwany po zwiększeniu wartości OLLAMA_NUM_PARALLEL.
Zakolejkowane. Pierwsze żądania odpowiadają szybko, a kolejne wykazują duży czas ttfb, po którym następuje normalne generowanie. 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 działanie – jest sygnaturą kolejki, a nie przeciążonego GPU.
Odrzucone. Klient otrzymuje odpowiedź http=503 niemal natychmiast, a treść komunikatu wygląda następująco:
{"error":"server busy, please try again. maximum pending requests exceeded"}Ten komunikat oznacza, że w momencie nadejścia żądania kolejka była pełna. Nie informuje on o stanie VRAM ani o modelu.
Jeden istotny limit: Ollama nie publikuje informacji o głębokości kolejki. ollama ps oraz punkt końcowy /api/ps raportują modele, które są załadowane, a nie żądania oczekujące. Dlatego kolejkę należy mierzyć od strony klienta, obserwując czas do pierwszego bajtu lub zliczając 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 wypadku trwa to minuty. Każdy klient HTTP podda się 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.
Należy ustawić kolejkę na wartość zbliżoną do liczby zadań, które serwer jest w stanie przetworzyć w czasie oczekiwania klienta. Wówczas 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, dashboard może zliczyć wystąpienie błędu, a administrator może go odczytać. Wartość tę należy wyznaczyć 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 znacznie głębsza niż ta wartość generuje jedynie błędy przekroczenia czasu.
Kiedy umieścić kolejkę przed Ollama
Wbudowana kolejka działa w trybie FIFO (pierwsze przyszł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ę punktów awarii. Rozwiązanie zewnętrzne należy zastosować w następujących sytuacjach:
- Wymagane jest priorytetyzowanie zadań. 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 serwera. Kolejka znajduje się w pamięci operacyjnej. Restart Ollama powoduje utratę wszystkich oczekujących żądań.
- Wymagane są rzeczywiste mechanizmy ponawiania prób z opóźnieniem (backoff), rejestrowane w miejscu dostępnym do późniejszej analizy.
Lżejszym rozwiązaniem jest użycie reverse proxy. W nginx dyrektywa limit_conn ogranicza liczbę jednoczesnych połączeń, a limit_req limituje częstotliwość żądań od 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 procesem roboczym (worker), który wywołuje Ollama. Jest to niezbędne, gdy żądania muszą przetrwać restart procesu. Skalowanie takiego rozwiązania pod rzeczywisty ruch jest osobnym zagadnieniem: planowanie własnego LLM dla wielu użytkowników zawiera niezbędne obliczenia, a uruchamianie Ollama na VPS opisuje podstawową instalację, na której opierają się te założenia.
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 zajęty slot, 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 zbudowane z myślą o wielu jednoczesnych użytkownikach działają inaczej. Przydzielają one pamięć podręczną KV w małych stronach na żądanie i dodają nadchodzące zapytania do już działającej partii, dzięki czemu zużycie pamięci podąża za rzeczywistym zapotrzebowaniem, zamiast opierać się na sztywnym podziale. 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 samej 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
Publikowane wartości liczby tokenów na sekundę pochodzą z cudzych jednostek GPU, modeli, kwantyzacji, długości kontekstu oraz promptów. Żaden z tych parametrów nie odpowiada Twoim, dlatego każdą przeczytaną liczbę należy traktować jako przybliżoną wskazówkę i przeprowadzić pomiar na własnej maszynie.
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 to z jednym slotem, a następnie ponownie przy poziomie współbieżności, którego faktycznie oczekujesz. 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 żądania. Przepustowość na żądanie zawsze spada wraz z dodawaniem kolejnych slotów. Pytanie brzmi, czy spadek ten nie przekracza akceptowalnego dla użytkowników poziomu. Pomiar liczby tokenów na sekundę w lokalnym modelu LLM omawia tę metodę bardziej szczegółowo, 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 nie kosztuje atakującego prawie nic: długie prompty, brak logowania, brak limitów prędkości, brak opłat. Własni użytkownicy otrzymują wtedy odpowiedzi 503 lub długo czekają, a maszyna jest stale obciążona.
Należy pozostawić nasłuchiwanie na interfejsie loopback i uzyskiwać dostęp przez tunel SSH lub sieć prywatną, albo umieścić przed usługą warstwę uwierzytelniania i limitowania zapytań. Zabezpieczanie punktu końcowego API Ollama omawia oba te rozwiązania. Kolejkę należy dostroić dopiero po wykonaniu tych kroków, ponieważ jej długość jest jedynie ustawieniem wydajnościowym i nie zapewnia żadnej ochrony.
FAQ
Dlaczego moje 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 do momentu zwolnienia slotu, 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ę, podczas gdy 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 żądań oczekujących osiągnęła OLLAMA_MAX_QUEUE, co domyślnie wynosi 512, więc najnowsze żądanie zostało odrzucone zamiast dodania 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, więc rzeczywistymi rozwiązaniami są: zwiększenie liczby slotów (jeśli dysponujesz odpowiednią ilością VRAM), zmniejszenie obciążenia wejściowego lub zastosowanie zewnętrznej kolejki, która może 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 KV cache, ponieważ Ollama uruchamia proces z całkowitym kontekstem równym długości kontekstu pomnożonej przez liczbę slotów. Jeśli wynik przestaje mieścić się w VRAM, Ollama przenosi warstwy do CPU, co spowalnia każde żądanie, nawet jeśli jest uruchomione bez konkurencji. Sprawdź ollama ps po wprowadzeniu zmian 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 uruchomieniowym. 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ź 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 powinienem ustawić?
Zacznij od 1 i zwiększaj wartość o jeden krok. 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 margines dla najdłuższego kontekstu, jaki obsługujesz. Następnie zmierz czas do pierwszego tokena oraz liczbę tokenów na sekundę przy tym ustawieniu pod rzeczywistym obciążeniem i cofnij się o jeden krok, jeśli prędkość na żądanie spadła poniżej poziomu akceptowalnego dla użytkowników.