Objawy i szybka klasyfikacja problemu
Skalowanie aplikacji web nie polega na samym dodaniu CPU, RAM albo drugiej instancji za NGINX. Jeżeli PostgreSQL, Redis albo load balancer mają złe limity, aplikacja może nadal zwracać błąd 502, błąd 504 lub losowo wylogowywać użytkowników; w dokumentacji NGINX Gateway Fabric przykładowe timeouty proxy to 5 sekund na połączenie, 60 sekund na odczyt i 30 sekund na wysyłanie (źródło: NGINX Documentation, 2026).
Po czym poznać źle wykonane skalowanie?
Najbardziej widoczne objawy są po stronie użytkownika: strona ładuje się raz szybko, raz bardzo wolno, formularz zapisu wisi kilkanaście sekund, koszyk w sklepie znika po odświeżeniu, a upload zdjęcia kończy się komunikatem o błędzie. W logach aplikacji zwykle widać wtedy timeout proxy, wyjątki połączeń do bazy albo błędy zapisu pliku na lokalnym dysku instancji.
- Błąd 502 zwykle oznacza, że NGINX, HAProxy albo inny reverse proxy nie dostał poprawnej odpowiedzi od procesu aplikacji, na przykład Node.js, PHP-FPM lub Gunicorn.
- Błąd 503 często wskazuje na brak dostępnych workerów, niezdrową instancję za load balancerem albo przeciążony pool procesów aplikacji.
- Błąd 504 oznacza timeout bramy i warto go diagnozować razem z logami proxy oraz poradnikiem błąd 504 gateway timeout – jak naprawić.
- Losowe wylogowania po dodaniu drugiego serwera wskazują na sesje lokalne zapisane w pamięci procesu, na dysku albo w pliku jednej instancji.
- Ginące uploady oznaczają często, że plik trafił na dysk lokalny serwera A, a kolejne żądanie użytkownika obsłużył serwer B.
- Wolne formularze po kliknięciu „Zapisz” sugerują blokadę w bazie, zewnętrzne API płatności, SMTP, ERP albo CRM.
Co pokazują metryki administratora?
Administrator powinien od razu sprawdzić CPU, RAM, I/O wait, liczbę aktywnych połączeń do PostgreSQL lub MySQL, czas odpowiedzi p95 i p99 oraz error rate. Z mojej praktyki administracyjnej: jeśli CPU aplikacji ma 25%, ale baza ma 95% I/O wait i pełny connection pool, dokładanie kolejnych instancji webowych tylko zwiększa korek.
Dlaczego po dodaniu serwera aplikacja działa gorzej?
Druga instancja ujawnia założenia, które działały tylko na jednej maszynie. Lokalny cache, pliki sesji, crony uruchamiane na każdym serwerze, katalog uploads na dysku C: lub /var/www/uploads oraz brak health checków są typowymi przyczynami regresji po skalowaniu poziomym.
Przed naprawą wykonaj backup bazy, zrzut konfiguracji NGINX lub Apache, listę zmiennych środowiskowych i kopię katalogu uploadów. Nie zmieniaj jednocześnie bazy, load balancera i kodu aplikacji, bo po wdrożeniu nie ustalisz, która zmiana naprawdę pomogła.
Najczęstsze przyczyny techniczne
Najczęstsze problemy ze skalowaniem mieszczą się w sześciu warstwach: kod aplikacji, baza danych, serwer WWW, load balancer, storage oraz usługi zewnętrzne. Większy serwer usuwa tylko część problemów, a bottleneck aplikacji zwykle przesuwa się tam, gdzie wcześniej nie było pomiaru.
Dlaczego baza danych blokuje wzrost wydajności?
Baza danych jest najczęstszym wąskim gardłem, bo wszystkie instancje aplikacji pytają ten sam zasób. Brak indeksów, wolne zapytania SQL, N+1 queries i locki potrafią zatrzymać sklep internetowy szybciej niż brak pamięci RAM na frontendzie.
Jak aplikacja traci stan między instancjami?
Aplikacja nie jest stateless, jeżeli zapisuje sesje, koszyki, pliki tymczasowe albo cache tylko lokalnie. Przykład: WordPress z uploadami w lokalnym wp-content/uploads będzie działał poprawnie na jednej maszynie, ale przy dwóch instancjach bez wspólnego storage użytkownik zobaczy brakujące miniatury.
Czy problemem może być konfiguracja NGINX, Apache albo PHP-FPM?
Tak, konfiguracja serwera WWW potrafi blokować skalowanie mimo zdrowego kodu. Sprawdź liczbę workerów, keep-alive, client_max_body_size, proxy_read_timeout, limity body size oraz przekazywanie nagłówków X-Forwarded-For i X-Forwarded-Proto.
Kiedy zewnętrzne API staje się bottleneckiem?
Płatności, SMTP, API ERP, CRM, geokodowanie, generowanie PDF i zewnętrzny system faktur mogą mieć limity, których nie widać w panelu hostingu. Jeżeli request czeka 8 sekund na odpowiedź operatora płatności, autoskalowanie aplikacji utworzy kolejne instancje, ale nie skróci oczekiwania na zewnętrzną usługę.
| Warstwa | Typowy objaw | Co sprawdzić najpierw | Bezpieczna naprawa |
|---|---|---|---|
| PostgreSQL lub MySQL | Wysoki p99, locki, pełny connection pool. | Slow query log, EXPLAIN, aktywne transakcje. | Indeksy, connection pooling, read replica dla odczytów. |
| NGINX lub Apache | Błąd 502, 504, zerwane uploady. | Timeout proxy, body size, liczba workerów. | Dostosowanie timeoutów i limitów po jednym parametrze. |
| Aplikacja Node.js, Laravel, Django | Wysokie CPU, wolne endpointy, N+1 queries. | Profilowanie endpointów i log czasu zapytań. | Optymalizacja kodu, cache, kolejka zadań. |
| Redis lub cache lokalny | Losowe sesje, niespójne wyniki, skoki RAM. | TTL kluczy, hit ratio, polityka eviction. | Wspólny Redis dla sesji i kontrolowana invalidacja. |
| Storage lokalny lub S3 | Ginące uploady, wolne pobieranie plików. | Miejsce na dysku, uprawnienia, czas zapisu. | Wspólny storage, S3, MinIO albo CDN dla statyków. |
- Autoskalowanie bez limitu kosztu może tworzyć kolejne instancje, gdy prawdziwym problemem jest pojedyncze zapytanie SQL bez indeksu.
- Load balancer bez health checków może wysyłać użytkowników na instancję, która formalnie działa, ale nie łączy się już z bazą danych.
- WebSocket wymaga osobnej kontroli timeoutów i obsługi upgrade, bo zwykłe ustawienia HTTP mogą zrywać długie połączenia czatu.
- PHP-FPM z małą wartością pm.max_children może zwracać 503, mimo że serwer ma wolny RAM i niski load average.
- CDN typu Cloudflare przyspiesza statyczne zasoby, ale nie naprawia wolnego zapisu zamówienia do PostgreSQL.
Jak rozpoznać bottleneck w bazie danych
Bottleneck bazy danych to punkt przeciążenia, charakteryzujący się wolnymi zapytaniami, blokadami transakcji, wysokim I/O wait i brakiem wolnych połączeń w connection pool. W PostgreSQL pierwsze miejsca diagnostyki to pg_stat_activity, pg_locks, EXPLAIN oraz log wolnych zapytań.
Jak znaleźć wolne zapytania SQL?
Włącz slow query log i ustaw próg ostrożnie: na start 500 ms dla aplikacji transakcyjnej albo 1000-2000 ms dla panelu administracyjnego z raportami. Następnie porównaj zapytania z największą liczbą wykonań i największym czasem łącznym, bo zapytanie trwające 80 ms, ale uruchamiane 2000 razy na minutę, może być groźniejsze niż pojedynczy raport trwający 5 sekund.
- W PostgreSQL sprawdź pg_stat_activity, aby zobaczyć aktywne zapytania, czas trwania i stan połączenia dla każdego procesu.
- W MySQL lub MariaDB włącz slow_query_log i analizuj wyniki przez mysqldumpslow albo Percona Toolkit.
- Dla zapytań SELECT wykonaj EXPLAIN lub EXPLAIN ANALYZE, ale nie uruchamiaj ciężkiego EXPLAIN ANALYZE na produkcji bez oceny ryzyka.
- Sprawdź brak indeksów na kolumnach używanych w WHERE, JOIN, ORDER BY i filtrach dat, na przykład created_at oraz user_id.
- Porównaj temat z osobnym poradnikiem jak znaleźć wolne zapytania SQL, jeżeli potrzebujesz checklisty dla PostgreSQL i MySQL.
Parafraza: PostgreSQL zaleca łączyć własny system statystyk z narzędziami systemowymi, takimi jak top, iostat i vmstat, a po znalezieniu słabego zapytania przejść do analizy przez EXPLAIN. PostgreSQL Global Development Group, Monitoring Database Activity, 2026
Jak rozpoznać locki i przepełniony connection pool?
Locki widać po transakcjach wiszących kilkadziesiąt sekund, statusie idle in transaction i rosnącej liczbie requestów czekających na zapis. Connection pool jest przepełniony, gdy aplikacja czeka na wolne połączenie, mimo że sama baza nie wykonuje już efektywnej pracy.
Kiedy read replica ma sens?
Read replica pomaga, gdy większość ruchu to odczyty: katalog produktów, wyszukiwarka, lista artykułów, panel raportowy. Nie pomoże, gdy problemem są zapisy zamówień, locki na tabeli sessions albo brak indeksu na tabeli orders liczącej 20 milionów rekordów.
Naprawa według przyczyny
Naprawa skalowania powinna zaczynać się od jednej potwierdzonej przyczyny, a nie od przypadkowego zwiększania parametrów. Dla każdej zmiany zapisz metrykę bazową: p95, p99, error rate, CPU, RAM, I/O wait, liczbę połączeń do bazy i czas odpowiedzi najważniejszych endpointów.
Jak naprawić problem z lokalnymi sesjami?
Sesje lokalne przenieś do Redis, bazy danych albo innego współdzielonego magazynu. Dla Laravel będzie to SESSION_DRIVER=redis, dla Django cache/session backend na Redis, a dla aplikacji Node.js typowy zestaw to express-session oraz connect-redis.
- Redis sesje powinny mieć TTL dopasowany do polityki logowania, na przykład 30 minut bezczynności dla panelu administracyjnego.
- Cookie sesyjne powinno mieć flagi Secure, HttpOnly i SameSite ustawione zgodnie z wymaganiami domeny oraz HTTPS.
- Uploady lokalne przenieś do S3, MinIO, Azure Blob Storage albo współdzielonego wolumenu, ale unikaj ręcznego kopiowania plików między instancjami.
- Cache w pamięci procesu zastąp Redis albo Memcached, jeżeli wynik musi być taki sam na kilku instancjach.
- Przy zmianie sesji zaplanuj krótkie okno serwisowe, bo użytkownicy mogą zostać wylogowani po migracji backendu sesji.
Parafraza: Redis jest magazynem danych w pamięci, używanym między innymi jako cache, message broker i streaming engine, z replikacją oraz opcjami trwałości na dysku. Redis Documentation, Open Source, 2026
Jak zmniejszyć obciążenie bazy danych?
Najpierw usuń najbardziej kosztowne zapytania, potem dodaj cache i dopiero później rozważ read replica. Przykład praktyczny: w sklepie WooCommerce indeks na meta_key i post_id może dać większy efekt niż podwojenie RAM, jeżeli lista produktów generuje setki zapytań do tabel postmeta.
Jak poprawnie ustawić health check i timeouty?
Endpoint /health powinien sprawdzać minimum: proces aplikacji, połączenie do bazy, dostęp do Redis i podstawowy zapis do katalogu tymczasowego, jeżeli aplikacja go wymaga. Nie rób health checka jako ciężkiego zapytania raportowego, bo monitoring sam może przeciążyć bazę.
W load balancerze ustaw osobne timeouty dla połączenia, odczytu i wysyłania. Dokumentacja NGINX opisuje, że timeout read dotyczy przerwy między kolejnymi operacjami odczytu, a nie całkowitego czasu pobierania odpowiedzi (źródło: NGINX Documentation, 2026). Dla konfiguracji sprawdź też load balancer – konfiguracja i typowe błędy.
Jak odciążyć request kolejką zadań?
Raporty PDF, importy CSV, mailing, miniatury obrazów i synchronizacja z ERP nie powinny blokować requestu użytkownika. Przenieś je do kolejki RabbitMQ, Redis Queue, Sidekiq, Celery albo BullMQ i zwracaj użytkownikowi status zadania.
- Endpoint HTTP powinien przyjąć żądanie, zapisać rekord zadania i zwrócić identyfikator job_id w czasie krótszym niż 500 ms.
- Worker kolejki powinien mieć limit równoległości, na przykład 2-4 zadania PDF na instancję, aby nie zabić CPU.
- Każde zadanie powinno być idempotentne, aby ponowne uruchomienie po awarii nie wysłało klientowi dwóch faktur.
- Błędy zewnętrznego SMTP lub API płatności powinny trafiać do retry z backoffem, na przykład 1 minuta, 5 minut i 30 minut.
- Cache HTTP, cache zapytań i CDN wdrażaj z invalidacją po zmianach, a nie z losowym czyszczeniem całego cache co godzinę.
Dla decyzji, czy w ogóle wdrażać cache, zobacz Redis cache – kiedy warto użyć.
Jak uniknąć utraty danych podczas naprawy
Bezpieczna naprawa to procedura, charakteryzująca się backupem, planem rollbacku, zmianą jednej warstwy naraz i testem odtworzenia danych. Największe ryzyko nie wynika z samego NGINX, PostgreSQL czy Redis, tylko z pośpiesznego wdrożenia kilku poprawek bez punktu powrotu.
Jak przygotować backup i plan rollbacku?
Backup musi być odtwarzalny, nie tylko wykonany. Dla PostgreSQL użyj pg_dump lub backupu fizycznego zgodnego z Twoją wersją, dla MySQL użyj mysqldump albo xtrabackup, a dla uploadów wykonaj spójną kopię katalogu lub bucketa S3.
- Przed zmianą schematu bazy zapisz migrację w GitLab, GitHub Actions albo innym systemie CI, aby znać dokładny commit wdrożenia.
- Przed zmianą NGINX wykonaj kopię plików site-enabled oraz sprawdź konfigurację poleceniem nginx -t na serwerze stagingowym.
- Przed migracją uploadów porównaj liczbę plików, rozmiar katalogu i próbkę adresów URL po stronie aplikacji.
- Przed zmianą Redis ustaw politykę maxmemory i eviction tak, aby sesje nie znikały po skoku ruchu.
- Przed dodaniem read replica sprawdź opóźnienie replikacji, bo odczyt z repliki może pokazać użytkownikowi stare dane zamówienia.
Czego nie zmieniać równocześnie?
Nie aktualizuj wersji frameworka, puli połączeń, timeoutów proxy i indeksów bazy w tym samym oknie serwisowym. Jeżeli po takim wdrożeniu p99 spadnie z 9 sekund do 2 sekund, nadal nie wiesz, czy pomógł indeks, cache, czy przypadkowo mniejszy ruch.
Jak chronić uploady i migracje?
Uploady traktuj jak dane użytkownika, nie jak pliki tymczasowe. Przy migracji z dysku lokalnego do S3 lub MinIO najpierw skonfiguruj zapis nowych plików w nowym miejscu, potem przenieś stare, a na końcu usuń zależność od lokalnego katalogu.
Weryfikacja i zapobieganie nawrotom
Weryfikacja naprawy polega na porównaniu zachowania aplikacji przed i po zmianie na tych samych ścieżkach użytkownika. Same średnie czasy odpowiedzi są niewystarczające, bo problem często ukrywa się w p95, p99 i błędach 5xx widocznych tylko pod większym ruchem.
Jak potwierdzić, że naprawa działa?
Wykonaj test obciążeniowy na najważniejszych ścieżkach: logowanie, wyszukiwanie, koszyk, zapis formularza, upload pliku i płatność. Dla aplikacji B2B dodaj eksport CSV, generowanie PDF oraz synchronizację z CRM, bo to one często blokują workery w godzinach pracy.
- Porównaj p95 i p99 dla każdego krytycznego endpointu, a nie tylko średnią odpowiedzi całej aplikacji.
- Sprawdź error rate dla kodów 500, 502, 503 i 504 osobno, ponieważ każdy wskazuje inną warstwę awarii.
- Zmierz liczbę połączeń do PostgreSQL lub MySQL przed testem, w szczycie testu i 5 minut po jego zakończeniu.
- Sprawdź I/O wait, zajętość dysku i długość kolejek workerów, bo CPU może wyglądać dobrze przy zablokowanym storage.
- Weryfikuj dane biznesowe, na przykład czy zamówienie ma płatność, fakturę i poprawny status po zakończeniu procesu.
Jakie alerty ustawić po incydencie?
Minimum to alerty na błędy 5xx, restart procesu, brak miejsca na dysku, wysoki czas odpowiedzi, pełny connection pool, opóźnioną kolejkę i przekroczenie kosztu autoskalowania. Prometheus, Grafana, Datadog albo New Relic są przydatne tylko wtedy, gdy alert prowadzi do konkretnej instrukcji działania.
Parafraza: NGINX opisuje timeouty connect, read i send jako niezależne wartości, a read i send dotyczą przerw między operacjami, nie całego czasu trwania requestu. F5 NGINX Documentation, Proxy Settings and Timeouts, 2026
Co wpisać do runbooka po awarii?
Runbook powinien zawierać lokalizację logów NGINX, aplikacji, bazy i workerów, polecenie wyłączenia instancji z load balancera, procedurę rollbacku konfiguracji oraz instrukcję odtworzenia backupu. Dodaj też progi alarmowe: na przykład p95 powyżej 1500 ms przez 5 minut, error rate powyżej 2% i zajętość dysku powyżej 85%.
- Po każdej kampanii marketingowej wykonaj przegląd wolnych zapytań, bo nowy ruch zmienia rozkład obciążenia tabel.
- Raz w miesiącu sprawdź, czy health check nadal testuje realne zależności aplikacji, a nie tylko zwraca statyczne OK.
- Przed dużą promocją uruchom test skalowania na kopii produkcyjnej konfiguracji, bez danych wrażliwych klientów.
- Po zmianach w cache dopisz zasady invalidacji, aby zespół wiedział, kiedy czyścić pojedynczy klucz, a kiedy cały namespace.
- Po incydencie zapisz jedną główną przyczynę, jedną zmianę naprawczą i jedną zmianę prewencyjną, zamiast ogólnego opisu „serwer był wolny”.
Najczęściej zadawane pytania
Dlaczego aplikacja nadal jest wolna po zwiększeniu serwera?
Najczęściej dlatego, że wąskie gardło jest poza samą maszyną aplikacyjną, na przykład w PostgreSQL, MySQL, zewnętrznym API albo kodzie generującym N+1 queries. Większy serwer nie naprawi brakujących indeksów ani blokujących transakcji. Najpierw porównaj p95, p99, liczbę połączeń do bazy i I/O wait.
Co oznaczają losowe wylogowania po dodaniu drugiej instancji?
To typowy objaw sesji zapisanych lokalnie na jednej maszynie. Użytkownik loguje się na instancji A, ale kolejne żądanie trafia na instancję B, która nie zna tej sesji. Rozwiązaniem jest Redis, baza danych albo inny współdzielony backend sesji.
Dlaczego uploady znikają po skalowaniu poziomym?
Pliki prawdopodobnie trafiają na lokalny dysk jednej instancji, a użytkownik przy kolejnym żądaniu trafia na inną. Uploady powinny być zapisane we wspólnym storage, usłudze obiektowej S3, MinIO albo Azure Blob Storage. Przed migracją porównaj liczbę plików i wykonaj backup.
Czy load balancer może powodować błędy 502?
Tak, load balancer może powodować błędy 502, jeżeli wysyła ruch do martwej instancji, ma złe timeouty albo nie przekazuje poprawnych nagłówków proxy. Sprawdź logi load balancera, logi aplikacji i endpoint /health. Przy WebSocket sprawdź też obsługę upgrade i długie timeouty połączeń.
Jak bezpiecznie naprawiać problem ze skalowaniem?
Najpierw wykonaj backup i zbierz metryki bazowe, potem zmieniaj jedną rzecz naraz. Po każdej zmianie porównaj p95, p99, error rate, liczbę połączeń do bazy i czasy krytycznych endpointów. Nie wdrażaj jednocześnie indeksów, Redis, timeoutów i aktualizacji frameworka.
Czy read replica zawsze przyspieszy aplikację?
Nie, read replica pomaga głównie przy dużej liczbie odczytów. Nie rozwiąże problemów z zapisami, lockami, pełnym connection pool ani zapytaniami bez indeksów. Najpierw sprawdź profil obciążenia bazy i dopiero potem przenoś wybrane SELECT-y na replikę.
Źródła i literatura
Jak weryfikować dokumentację techniczną?
Dla problemów produkcyjnych korzystaj z dokumentacji producenta, logów systemowych i własnych pomiarów. Blog może podsunąć trop, ale decyzję o zmianie timeoutu, indeksu albo strategii sesji opieraj na dokumentacji NGINX, PostgreSQL, Redis oraz metrykach z Twojego środowiska.
Które źródła użyto?
- PostgreSQL Global Development Group, Monitoring Database Activity, dokumentacja PostgreSQL, 2026: https://www.postgresql.org/docs/current/monitoring.html.
- F5 NGINX, Reverse Proxy and Proxy Timeout Documentation, dokumentacja NGINX, 2026: https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/.
- F5 NGINX Gateway Fabric, Proxy Settings Policy and Timeouts, dokumentacja NGINX, 2026: https://docs.nginx.com/nginx-gateway-fabric/traffic-management/proxy-settings/.
- Redis Documentation, Open Source Redis use cases and data store overview, 2026: https://redis.io/docs/latest/get-started/.
- IETF, RFC 9110 HTTP Semantics, status codes and HTTP behavior, 2022: https://www.rfc-editor.org/rfc/rfc9110.

