Strona nie działa? Zadzwoń.

Tworzenie Stron Rzeszów to marka firmy Prownex IT Services Sp. z o.o. (NIP 8161717256). Tworzymy strony internetowe i sklepy dla małych i średnich firm z Rzeszowa i okolic, utrzymujemy je na własnym hostingu prowde All Inclusive oraz naprawiamy, zabezpieczamy i przenosimy istniejące strony WordPress.

„Error establishing a database connection” w sklepie WooCommerce

Sprawdziliśmy na sklepie z 30 000 produktów, co się dzieje, gdy baza danych działa na ustawieniach domyślnych, a do sklepu wchodzi naraz dużo osób. Od 150 jednoczesnych wejść klienci dostawali stronę błędu zamiast sklepu. Po dopasowaniu konfiguracji bazy było zero błędów przy 200.

Ilustracja: baza danych z wąską bramką, do której prowadzą połączenia z kilku okien przeglądarki, koszyk sklepu i trójkąt ostrzegawczy

Opublikowano: 03.11.2026 · stan na listopad 2026 · Redakcja Tworzenie Stron Rzeszów

W skrócie

Sklep
WordPress z WooCommerce, 30 000 produktów, serwer z 6 rdzeniami (test w naszym środowisku)
Objaw
przy dużym ruchu zamiast sklepu biała strona „Error establishing a database connection”, po chwili sklep znów działa
Przyczyna
baza danych na ustawieniach domyślnych przyjmuje mniej połączeń, niż serwer wpuszcza naraz odwiedzających
Rozwiązanie
konfiguracja bazy dopasowana do serwera i sklepu oraz porządna strona awaryjna
Efekt
przy 200 jednoczesnych wejściach zero błędów zamiast 95%, 40 obsłużonych stron na sekundę zamiast 27

Sklep znika w najlepszym momencie

Właściciel sklepu widzi zwykle jedno: w najlepszym momencie, w czasie promocji, po wysyłce newslettera albo w Black Friday, sklep na kilka minut znika. Zamiast produktów pojawia się biała strona z jednym zdaniem: „Error establishing a database connection”. Po chwili wszystko wraca do normy i trudno ustalić, co się stało.

Chcieliśmy to sprawdzić w liczbach, a nie na wyczucie: przy jakim ruchu sklep na typowej, domyślnej konfiguracji bazy danych zaczyna odrzucać klientów, co wtedy pokazują logi i czy zmiana konfiguracji naprawdę rozwiązuje problem.

Jak sprawdzaliśmy

Postawiliśmy sklep jak u klienta: 30 000 produktów z opisami, kategoriami i filtrami, typowy serwer. Program udawał odwiedzających: oglądali kategorie, karty produktów, filtrowali po kolorze i rozmiarze, szukali w wyszukiwarce.

Liczbę osób zwiększaliśmy stopniowo: 50, 100, 150, 175 i 200 naraz. Każdy „odwiedzający” klikał kolejną stronę od razu po załadowaniu poprzedniej. Prawdziwi klienci robią przerwy, więc 200 takich odwiedzających odpowiada dużo większej liczbie osób w sklepie.

Co widzieli klienci przy rosnącym ruchu

Odwiedzających naraz Co widzieli klienci
50 i 100 sklep działał, wolniej przy 100
150 pierwsze strony błędu (prawie 4% wejść)
175 strona błędu przy 94% wejść
200 strona błędu przy 95% wejść

Tak wygląda sklep dla klienta w tym momencie

Biała strona z jednym zdaniem „Error establishing a database connection”, w karcie przeglądarki tytuł „Database Error”. Serwer zwraca przy tym kod błędu 500.

Polski sklep pokazuje ten komunikat po angielsku. Informacja o języku strony jest zapisana w bazie danych, z którą właśnie nie ma połączenia.

Zrzut ekranu: szara strona z jedną białą ramką i napisem „Error establishing a database connection”, bez logo i treści sklepu

Dlaczego tak się dzieje

Baza danych sklepu ma ograniczoną liczbę „okienek obsługi”, czyli połączeń. Każda osoba, której strona właśnie się ładuje, zajmuje jedno okienko do końca ładowania. Po instalacji baza ma 151 okienek. Serwer, który generuje strony, był ustawiony tak, żeby obsługiwać naraz do 180 osób.

Dopóki ruch jest mały, nikt tej różnicy nie zauważa. W szczycie strony ładują się dłużej, osób w kolejce przybywa i w pewnym momencie brakuje okienek: baza odmawia, a klient dostaje stronę błędu.

Ustawienia domyślne nie są błędem producenta. To wartości startowe dla małego serwera. Ktoś musi je dopasować do tego, ile osób serwer wpuszcza i ile danych ma sklep.

Co nie było oczywiste

  1. Ta sama strona błędu pojawia się przy trzech różnych awariach. Przy przeciążeniu, przy złym haśle do bazy i przy wyłączonej bazie WordPress pokazuje identyczną stronę. Typowa reakcja to zmiana haseł albo przywracanie kopii zapasowej, czyli strata czasu.
  2. WordPress nic nie zapisuje, a jedyna wskazówka w logach radzi coś odwrotnego. W logach serwera jedyny ślad sugeruje „dołożyć procesów”, co pogłębiłoby problem.
  3. Nawet administrator nie może wejść do bazy. W czasie przeciążenia zalogowanie się administratora udało się w 3 próbach na 10.
  4. Błędy nie odciążają serwera, tylko go dobijają. W czasie zalewu błędów sklep obsługiwał o jedną trzecią mniej udanych stron niż po naprawie.
  5. Nie każda popularna porada pomaga. Dwie często polecane zmiany w konfiguracji dały w pomiarze niewiele albo nic. Dlatego każdą zmianę sprawdzaliśmy tym samym testem.

Co zrobiliśmy

  • Konfiguracja bazy dopasowana do serwera: więcej połączeń niż osób, które serwer wpuszcza naraz, z zapasem; więcej pamięci, żeby baza nie czytała danych z dysku; zapis wolnych zapytań, żeby było wiadomo, co spowalnia sklep.
  • Strona awaryjna zamiast białej strony z błędem: klient widzi po polsku „Sklep jest chwilowo przeciążony… Odśwież stronę za minutę, Twój koszyk na to poczeka” i numer telefonu. Google dostaje sygnał, że przerwa jest chwilowa, a każde takie zdarzenie zostaje zapisane.
Zrzut ekranu: strona awaryjna sklepu testowego z nagłówkiem „Sklep jest chwilowo przeciążony”, prośbą o odświeżenie za minutę i przykładowym numerem telefonu

Efekt przed zmianą i po niej

Co mierzyliśmy Przed Po
Strona błędu przy 200 osobach naraz 95% wejść 0
Obsłużone strony na sekundę przy 200 osobach 27 40
3 minuty ciągłego ruchu 200 osób nie do utrzymania 6 967 stron, zero błędów

Sklep po zmianie nie jest szybszy dla każdego klienta: przy 200 osobach naraz strona ładuje się średnio ok. 3 s, a wyszukiwarka kilkanaście sekund, bo serwer ma swoją moc. Różnica polega na tym, że każdy klient dostaje sklep, a nie stronę błędu.

Wnioski dla właściciela sklepu

  • Domyślna konfiguracja bazy to punkt startowy, a nie ustawienie pod sklep. Sklep pada dokładnie wtedy, gdy przychodzi najwięcej klientów.
  • Komunikat „Error establishing a database connection” nie mówi, co się stało. Zanim ktoś zacznie zmieniać hasła albo przywracać kopię, warto, żeby spojrzał na to ktoś, kto wie, gdzie szukać.
  • Serwer to całość: liczba odwiedzających obsługiwanych naraz, połączenia bazy, pamięć i strona awaryjna muszą do siebie pasować. Tym zajmuje się hosting, który ktoś naprawdę konfiguruje, a nie tylko instaluje.
  • Jeśli sklep ma obsłużyć jeszcze więcej osób, kolejnym krokiem jest pamięć podręczna stron. Tego w tym teście nie mierzyliśmy.

Twój sklep pokazuje ten komunikat albo znika w czasie promocji? Opisz objawy w formularzu, a sprawdzimy serwer i bazę. Więcej o tym, jak budujemy i utrzymujemy sklepy internetowe na WooCommerce i jak przebiega naprawa strony WordPress.

Opis techniczny: logi, liczniki i konfiguracja bazy

Dla administratorów: pełny przebieg testu z dosłownymi komunikatami, które można porównać z logami własnego serwera.

Środowisko testu (stan na listopad 2026)

nginx 1.30, PHP-FPM 8.3 (opcache włączony), WordPress 7.1.4 (pl_PL), WooCommerce 11.2.1, motyw Storefront, MariaDB 11.4.13 bez zmian w konfiguracji, każdy w osobnym kontenerze; 6 vCPU, 12 GB RAM. 30 002 produkty proste z kategoriami i atrybutami pa_kolor, pa_rozmiar, pa_marka, baza 267 MB danych i indeksów (wp_posts 94 MB, wp_postmeta ok. 600 tys. wierszy).

PHP-FPM ustawione „na większy ruch”:

[www]
pm = dynamic
pm.max_children = 180
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 40
listen.backlog = 1024

MariaDB w konfiguracji po instalacji:

zmiennawartość domyślna
max_connections151
innodb_buffer_pool_size128 MB
tmp_table_size / max_heap_table_size16 MB
wait_timeout28 800 s
slow_query_log / long_query_timewyłączony / 10 s
thread_handlingone-thread-per-connection

Jak mierzyliśmy

Zamknięta pętla: N wirtualnych odwiedzających, każdy wysyła kolejne żądanie od razu po odpowiedzi. Mieszanka adresów jak w dniu promocji: 30% kategorie (z orderby i stronicowaniem), 25% karty produktów, 20% /sklep/?filter_kolor=…&filter_rozmiar=…, 15% wyszukiwarka /?s=…&post_type=product, 10% strona główna, to samo ziarno losowania w każdym przebiegu. Po minucie rozgrzewki kroki 50, 100, 150, 175 i 200 jednoczesnych odwiedzających po 45 s. Liczniki bazy co sekundę przez jedno stałe połączenie administracyjne:

SELECT VARIABLE_NAME, VARIABLE_VALUE FROM information_schema.GLOBAL_STATUS
WHERE VARIABLE_NAME IN ('THREADS_CONNECTED', 'THREADS_RUNNING', 'MAX_USED_CONNECTIONS',
  'CONNECTION_ERRORS_MAX_CONNECTIONS', 'ABORTED_CONNECTS', 'INNODB_BUFFER_POOL_READS',
  'INNODB_BUFFER_POOL_READ_REQUESTS', 'CREATED_TMP_DISK_TABLES', 'SLOW_QUERIES');

Warianty, każdy po restarcie bazy i rozgrzewce: A domyślna, B tylko max_connections = 250, C konfiguracja dopasowana do serwera, D jak C plus pula wątków.

Wyniki

jednocześniestrony/s (udane)medianap95odpowiedzi z błędem
5036,10,79 s5,84 s0
10036,51,52 s13,10 s0
15038,22,30 s18,38 s68 (3,8%)
17529,23,63 s18,15 s21 236 (94,2%)
20027,44,87 s14,25 s24 356 (95,2%)

Wariant A (domyślny). Wysoki odsetek błędów przy 175 i 200 wynika z tego, że strona błędu wraca w kilka milisekund, a odwiedzający w teście od razu wysyła kolejne żądanie (jak ktoś naciskający F5 albo bot). Mediana i p95 dotyczą tylko udanych odpowiedzi. Liczniki po rampie: Max_used_connections = 152, Aborted_connects = 45 660, Connection_errors_max_connections = 35 909.

wariant150: strony/s, p95, błędy175: strony/s, p95, błędy200: strony/s, mediana, p95, błędy
A domyślna38,2 / 18,4 s / 3,8%29,2 / 18,2 s / 94,2%27,4 / 4,87 s / 14,3 s / 95,2%
B max_connections = 25037,9 / 18,8 s / 038,8 / 21,8 s / 039,7 / 3,27 s / 22,3 s / 0
C konfiguracja dopasowana39,2 / 17,7 s / 039,2 / 19,8 s / 040,1 / 3,35 s / 20,7 s / 0
D jak C + pula wątków36,9 / 7,4 s / 036,3 / 8,3 s / 037,2 / 5,37 s / 9,1 s / 0

Test stabilności wariantu C: 200 jednoczesnych odwiedzających przez 180 s, 6 967 odpowiedzi 200, średnio 38,7 strony na sekundę, Connection_errors_max_connections = 0, Aborted_connects = 0, Max_used_connections = 184.

Co zapisały logi

Odpowiedź dla odwiedzającego (tytuł „Database Error”, nagłówek „Error establishing a database connection”):

HTTP/1.1 500 Internal Server Error
Content-Type: text/html; charset=UTF-8
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0, no-store, private

nginx, sam kod odpowiedzi, w logu błędów nic:

"GET / HTTP/1.1" 500 2494 "-" "Mozilla/5.0 ..."

PHP-FPM, tylko ostrzeżenia o puli procesów:

WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 32 children, there are 0 idle, and 175 total children
WARNING: [pool www] server reached pm.max_children setting (180), consider raising it

WordPress przy WP_DEBUG = false nie zapisuje nic. Po włączeniu WP_DEBUG i WP_DEBUG_LOG w wp-content/debug.log (45 s przy 200 odwiedzających, 14 564 linie):

11466 x  PHP Warning:  mysqli_real_connect(): (08004/1040): Too many connections in /var/www/html/wp-includes/class-wpdb.php on line 1990
 3098 x  PHP Warning:  mysqli_real_connect(): (HY000/1040): Too many connections in /var/www/html/wp-includes/class-wpdb.php on line 1990

MariaDB, log błędów (35 909 wpisów, tyle samo co Connection_errors_max_connections):

[Warning] Aborted connection 0 to db: 'unconnected' user: 'unauthenticated' host: 'connecting host' (Too many connections)

Klient mariadb administratora w czasie przeciążenia (7 z 10 prób):

ERROR 2002 (HY000): Received error packet before completion of TLS handshake. The authenticity of the following error cannot be verified: 1040 - Too many connections

Przyczyna

WordPress łączy się z bazą na początku każdego żądania (wpdb::db_connect()) i zamyka połączenie dopiero po wygenerowaniu strony. Liczba połączeń w danej chwili jest więc równa liczbie żądań PHP w toku, a górną granicę wyznacza pm.max_children wszystkich pul PHP-FPM korzystających z tej bazy. Przy 6 rdzeniach sklep obsługuje około 36 do 40 stron na sekundę. Powyżej tego żądania czekają na procesor, PHP-FPM dokłada procesy („spawning 32 children”), każdy otwiera połączenie, a po przekroczeniu max_connections MariaDB odrzuca kolejne z błędem 1040.

Pułapki dla administratora

  • Limit 151 nie oznacza 151 żądań sklepu. Pulę dzielą z WordPressem cron, kopie zapasowe, panel administratora, monitoring i inne strony na tym samym serwerze bazy. Liczyć trzeba sumę pm.max_children wszystkich pul plus zapas.
  • Strona błędu jest identyczna co do bajtu przy przekroczonym limicie, błędnym haśle w wp-config.php i wyłączonej bazie (ta sama suma MD5, ten sam kod 500).
  • WordPress wycisza błąd. Przy WP_DEBUG = false wywołuje @mysqli_real_connect(), więc kod 1040 nie trafia do żadnego logu PHP. Ostrzeżenie PHP-FPM „consider raising it” prowadzi w złą stronę: więcej procesów to więcej połączeń.
  • Log MariaDB notuje tylko 79% odmów. Odrzucone od razu (08004/1040) trafiają do logu i licznika Connection_errors_max_connections (35 909). Odrzucone po uwierzytelnieniu (HY000/1040) nie zostawiają wpisu. Pełną liczbę pokazuje tylko Aborted_connects (45 660).
  • Zapasowe połączenie administratora nie działa przy zalewie połączeń. MariaDB dopuszcza max_connections + 1, ostatnie dla użytkownika z uprawnieniem CONNECTION ADMIN, ale przy tysiącach prób na sekundę jest stale zajęte. Klient MariaDB 11.4 domyślnie zestawia TLS, więc administrator widzi komunikat o TLS, a nie „Too many connections”.
  • Domyślnie baza nie zapisuje nawet zapytań dłuższych niż 10 s. W wariancie B licznik Slow_queries doszedł do 743 i żadne nie trafiło do logu. Z progiem 1 s widać winowajców: główną pętlę produktów (SQL_CALC_FOUND_ROWS), zapytania nawigacji warstwowej i liczniki filtrów.
  • Bufor InnoDB 1 GB zamiast 128 MB zmniejszył odczyty z dysku z 7,8 GB do zera, ale przepustowość wzrosła tylko o 1 do 4%, bo ograniczeniem był procesor. Sam bufor nie usuwa błędu połączeń.
  • tmp_table_size i max_heap_table_size 64 MB nie zmieniły nic: około 2 000 tabel tymczasowych na dysku w każdym wariancie, bo tabele z kolumnami TEXT/BLOB (post_content, meta_value) MariaDB tworzy na dysku niezależnie od tych limitów.
  • Pula wątków (thread_handling = pool-of-threads, thread_pool_size = 6) skróciła p95 przy 200 żądaniach z 20,7 s do 9,1 s, ale mediana wzrosła z 3,35 s do 5,37 s, a przepustowość spadła o około 7%.
  • Wyszukiwarka WooCommerce (LIKE '%…%' na post_title, post_excerpt, post_content) przegląda całą tabelę produktów: mediana 15 do 18 s pod obciążeniem w każdym wariancie. Pomoże indeks pełnotekstowy albo zewnętrzna wyszukiwarka, nie konfiguracja bazy.

Konfiguracja MariaDB po zmianie

[mysqld]
# Limit połączeń większy niż suma pm.max_children (180) z zapasem na cron, kopie i administratora
max_connections         = 250
# Cała baza sklepu (267 MB danych i indeksów) mieści się w pamięci
innodb_buffer_pool_size = 1G
innodb_log_file_size    = 256M
table_open_cache        = 4000
table_definition_cache  = 2000
# Bezczynne połączenia nie wiszą 8 godzin
wait_timeout            = 300
# Wolne zapytania trafiają do logu
slow_query_log          = 1
slow_query_log_file     = /var/lib/mysql/slow.log
long_query_time         = 1

Reguła: max_connections większe niż suma pm.max_children wszystkich pul PHP-FPM korzystających z bazy plus połączenia cron, kopii i administracji. Z buforem 1 GB i 182 połączeniami naraz proces bazy zajmował w szczycie około 0,9 GB.

Diagnostyka bez włączania debugowania WordPressa

SHOW GLOBAL VARIABLES LIKE 'max_connections';
SHOW GLOBAL STATUS WHERE Variable_name IN
  ('Max_used_connections', 'Threads_connected', 'Connection_errors_max_connections', 'Aborted_connects', 'Slow_queries');

Max_used_connections równe max_connections + 1 i rosnący Aborted_connects oznaczają, że limit był wyczerpany, nawet jeśli w logu bazy nie ma wpisów.

Strona awaryjna wp-content/db-error.php

<?php
error_log('WordPress: brak połączenia z bazą danych, ' . ($_SERVER['REQUEST_METHOD'] ?? 'CLI') . ' ' . ($_SERVER['REQUEST_URI'] ?? ''));
if (!headers_sent()) {
    http_response_code(503);
    header('Retry-After: 60');
    header('Cache-Control: no-store, max-age=0');
    header('Content-Type: text/html; charset=utf-8');
}
// dalej strona HTML po polsku: „Sklep jest chwilowo przeciążony…”, telefon

Sprawdzone pod obciążeniem w wariancie A: HTTP/1.1 503 Service Unavailable z Retry-After: 60, polska treść i wpis przy każdym incydencie:

NOTICE: PHP message: WordPress: brak połączenia z bazą danych, GET /produkt/…

Gdy sklep pokazuje ten błąd

  • Naprawa strony WordPress (także po włamaniu)

    Gdy strona pokazuje błąd, nie da się zalogować do panelu albo strona została zainfekowana i trzeba ją wyczyścić.

    Wycena indywidualna

    • diagnoza przyczyny: wtyczki, motyw, wersja PHP, baza danych, logi serwera
    • kopia plików i bazy danych przed zmianami
    • naprawa błędu albo usunięcie złośliwego kodu i zamknięcie luki
    • kontrola panelu, formularza i najważniejszych podstron po naprawie

    Co dostajesz

    Przed naprawą: cenę do akceptacji. Po naprawie: krótki opis przyczyny i tego, co zrobić, żeby problem nie wrócił.

  • Hosting indywidualny

    Dla większych projektów: stron, które nie działają na WordPressie, aplikacji napisanych we własnym PHP i stron, których po kontroli kompatybilności nie da się obsłużyć w pakietach All Inclusive.

    Wycena indywidualna

    • serwer i konfiguracja dopasowane do projektu
    • zakres opieki, kopii i aktualizacji ustalany razem z Tobą

Zadzwoń i powiedz, czego potrzebuje Twoja strona

Rozmawiamy o nowej stronie, hostingu albo problemie z obecną stroną. Wycenę nowej strony dostajesz bezpłatnie w 24 h.

Telefon (polski) +48 501 250 220

Dostępni: poniedziałek–piątek, 9:00–17:00