Помилки 504 у WordPress рідко є «проблемою WordPress». Це симптом: запит сторінки пройшов у ваш стек і не встиг повернутися, поки таймер шлюзу не вичерпався. Часто база даних — це місце, де запит «помер» — тихо, чемно й із достатньою кількістю логів, щоб зіпсувати вам післяобідню порцію роботи.
Якщо ви вибираєте між MySQL і MariaDB для WordPress під час сплесків трафіку, ось відверта правда: будь-яка з них може витримати, і будь-яка може впасти. База даних, яка «ламається першим», зазвичай — та, яку ви налаштували як у 2014 році, розгорнули як улюбленця й моніторите як слух. Говоритимемо про режими відмов, а не про бренд-лояльність.
Що насправді означає помилка 504 у виробництві
504 — це не «WordPress вичерпав час очікування». Це ваш шлюз (зазвичай Nginx, інколи ALB, інколи CDN), який каже: «Я запитав upstream про відповідь, і він не відповів достатньо швидко». Upstream може бути PHP-FPM, який може бути заблокований на MySQL, який може бути заблокований на диску, який може бути заблокований на м’ютексі, який може бути заблокований на ваших сумнівних рішеннях.
На типовому стеку WordPress:
- Клієнт звертається до CDN або балансувальника навантаження.
- Запит іде до Nginx/Apache.
- Динамічний шлях переходить до PHP-FPM.
- PHP виконує роботу додатка і звертається до MySQL/MariaDB.
- БД читає з buffer pool або диска, блокує рядки, пише redo/undo та повертає результати.
Під час сплеску найслабша ланка — не обов’язково найповільніший компонент; це той компонент, який найменш граціозно скидатиме навантаження. Насичена БД зазвичай не падає швидко. Вона падає через черги. Черги виглядають як «все ще працює, просто повільніше», що перетворюється на «усе вичерпало час очікування», що перетворюється на «канал інциденту став груповою терапією».
Є два основні способи, як база даних перетворює сплеск на помилки 504:
- Тиск підключень: занадто багато одночасних PHP-воркерів, які намагаються підключитися або виконати запити, що призводить до вибуху потоків, переключення контексту або виснаження ресурсів.
- Збільшення латентності: запити стають повільнішими через дискові операції, суперечку за блокування, пропуски в buffer pool, відставання purge, тиск на redo лог або просто погані плани виконання.
Отже «хто ламається першим» насправді означає: який движок + конфігурація дають вам кращу хвостову латентність і передбачувану поведінку, коли черга починає формуватися.
MySQL vs MariaDB під час стрибка трафіку: хто ламається першим (і чому)
Базова реальність: WordPress — не бенчмарк бази даних
WordPress — це не чистий OLTP. Це багато дрібних читань, деякі записи і трюк під назвою wp_options, який може стати найгарячішою таблицею в будівлі. Також там є SQL від плагінів різної моральної якості.
Що має значення під час сплесків:
- Обробка підключень: модель «потік на підключення» проти пулінгу потоків/поведінки пулінгу.
- Поведінка InnoDB під навантаженням: блокування рядків, метадані, purge, скидування.
- Передбачуваність оптимізатора: стабільні плани, коректні індексні рішення.
- Спостережуваність та інструменти: чи бачите ви, що відбувається, до того як 504 перетворяться на повний колапс?
MySQL: передбачуваний, якщо тримати його простим; небезпечний, якщо залишити «за замовчуванням»
Сучасний MySQL (8.x) надійний для WordPress. Він також безапеляційно складний. Параметри за замовчуванням розраховані на запуск скрізь, а не на те, щоб витримати, коли ваша домашня сторінка потрапляє на головну сторінку інтернету.
Де MySQL зазвичай поводиться краще під сплесками:
- Стабільний InnoDB з зрілою інструментальністю (Performance Schema, sys schema).
- Покращення оптимізатора за роки, які часто допомагають у змішаних навантаженнях.
- Зрілість екосистеми реплікації та інструментів у багатьох організаціях.
Де MySQL часто ламається першим (у реальних WordPress-стеках):
- Шторми підключень, коли PHP-FPM збільшує воркерів і кожен відкриває підключення до БД; ви стаєте обмежені CPU на потоках та м’ютексах задовго до того, як «CPU вичерпається» в звичному сенсі.
- Затримки через I/O, коли buffer pool замалий, а redo log/флашинг налаштовані як на ноутбуку.
- Накопичення метаданих блокувань через DDL або довгі транзакції (часто від «безпечних» адмін-операцій під час піку).
MariaDB: іноді більш поблажлива до конкуренції, іноді — пастка сумісності
MariaDB починалась як форк із знайомим обличчям. З часом вона стала власною базою даних із власною особистістю. Для WordPress практичне відмінність під сплесками найчастіше пов’язана з поведінкою при конкуренції (особливо якщо ви використовуєте thread pool у MariaDB) та оперативною звичністю в залежності від того, що ваша команда вже експлуатує.
Де MariaDB зазвичай поводиться краще під сплесками:
- Thread pool (в багатьох збірках MariaDB) може зменшити «трясіння» потоків при великій кількості підключень, обмежуючи активні воркери й плануючи виконання.
- Операційні регулювання, якими команди інколи легше оперують, залежно від пакування дистрибутива та значень за замовчуванням.
Де MariaDB ламається першою (знову ж у полі):
- Припущення «це drop-in MySQL», які ламаються на крайових випадках поведінки SQL, системних змінних або очікуваннях інструментів — особливо якщо ви міксуєте компоненти екосистеми, що очікують семантики MySQL 8.
- Сюрпризи планів запитів, якщо ви звикли до поведінки оптимізатора MySQL і не перевіряєте з
EXPLAINпісля апгрейдів. - Невідповідності реплікації/GTID у змішаних середовищах під час міграцій, що перетворює «просто додати репліку» на «чому репліка така зла?»
Якщо ви хочете прямої відповіді: під раптовим трафіковим сплеском перше, що зазвичай ламається — це керування підключеннями, а не «MySQL проти MariaDB». MariaDB з thread pool може триматися довше під штурмом підключень. MySQL також може впоратися, але часто потребує від вас продуманих кроків: обмежити PHP-FPM воркери, використовувати пуління (ProxySQL або подібне) і припинити вважати max_connections функцією продуктивності.
Жарт №1: Трафіковий сплеск — це природа, що питає, чи «це працює на staging» також платить за оренду.
Факти та історія, що досі важливі для вашого інциденту
- Факт 1: MariaDB відокремилась від MySQL після того, як Oracle придбав Sun Microsystems (та зокрема MySQL) у 2010 році, через побоювання за майбутнє управління MySQL.
- Факт 2: MySQL 8.0 запровадив значні зміни — переробку словника даних, покращений Performance Schema та багато поліпшень оптимізатора — тому мислення «MySQL 5.7» — це застаріла модель.
- Факт 3: Номери версій MariaDB свідомо розійшлися (10.x) і не порівнюються напряму з MySQL 8.0; вважати «вищий = новіший» призведе до хибних очікувань.
- Факт 4: У багатьох дистрибутивах пакети «mysql» з часом стали мета-пакетами, що вказують або на MySQL, або на MariaDB; команди інколи виявляли, що вони випадково працювали на MariaDB. Це весело рівно один раз.
- Факт 5: Історично WordPress орієнтувався на сумісність з MySQL, але сучасний WordPress не захистить вас від поганого SQL від плагінів; движок бази даних не врятує вас від плагіна, що робить повні сканування таблиць на кожен запит.
- Факт 6: InnoDB став за замовчуванням движком MySQL роками тому, і майже всі серйозні робочі навантаження WordPress повинні використовувати InnoDB; MyISAM у продакшені — це капсула часу з гострими краями.
- Факт 7: Query cache (стара функція MySQL) була видалена з MySQL 8 і вимкнена/неактуальна в більшості сучасних налаштувань; якщо хтось радить її увімкнути — вони посилаються на фольклор.
- Факт 8: «Більше реплік» не вирішить проблему конкуренції записів; WordPress має патерн гарячих точок записів (сесії, options, transient updates), що може стати вузьким місцем первинного сервера, навіть якщо читання відвантажені.
- Факт 9: Модель «потік на підключення» може стати обмеженою планувальником CPU задовго до того, як графік CPU виглядає «високим»; графік може брешуть, бо час витрачається на переключення контексту й очікування.
Швидкий план діагностики: знайти вузьке місце за кілька хвилин
Порядок, який виграє інциденти. Не тому, що він теоретично чистий, а тому, що він швидко звужує пошук.
Перш за все: доведіть, де відбувається таймаут
- Логи шлюзу (Nginx/ALB/CDN): це таймаут upstream чи клієнтський?
- Стан PHP-FPM: воркери насичені, спрацьовує slowlog або застрягають на БД?
- БД: підключення та активні запити: чи вибухають потоки, чи кілька запитів зависли?
По-друге: вирішіть «шторм підключень» vs «повільні запити» vs «блокування»
- Шторм підключень: багато сплячих/підключаючихся потоків, велике Threads_connected, зростання CPU sys time, багато коротких запитів вичерпують час очікування, PHP-FPM на максимумі.
- Повільні запити / I/O: пропуски buffer pool, висока дискова латентність, великі InnoDB читання, довгі часи запитів без явних блокувань.
- Блокування: «Waiting for table metadata lock», «Waiting for record lock», багато запитів блоковані за одним записувачем, таймаути очікування блокувань.
По-третє: зупиніть кровотечу безпечно
- Скиньте навантаження на краю: увімкніть кеш, обмежте швидкість зловмисних endpoint-ів, тимчасово відключіть дорогі маршрути (пошук, wp-cron через веб, XML-RPC якщо доречно).
- Обмежте конкуренцію: зменшіть кількість PHP-FPM дітей, якщо БД тоне; дозволяти нескінченним PHP-воркерам бити по БД — це не «scale».
- Вбивайте найгірших виконавців: завершіть той запит, що тримає блокування або виконується хвилини, але переконайтесь, що це не критичний DDL або бекап.
Парафразована ідея (John Allspaw): Надійність — це властивість усієї системи, а не одного компонента.
Практичні завдання: команди, виводи та рішення (12+)
Усе нижче розраховано на запуск у типовому Linux-хості з WordPress + Nginx + PHP-FPM + MySQL/MariaDB. Налаштуйте шляхи й імена сервісів для вашого дистрибутива.
Завдання 1: Підтвердити, що 504 — це таймаут upstream (не DNS, не клієнт)
cr0x@server:~$ sudo tail -n 20 /var/log/nginx/error.log
2025/12/29 10:11:45 [error] 1187#1187: *28491 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 203.0.113.10, server: example.com, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock", host: "example.com"
Що це означає: Nginx чекав PHP-FPM і не отримав заголовків вчасно. БД може бути причиною, але негайною точкою спаду є латентність upstream.
Рішення: Перейдіть до перевірки насиченості PHP-FPM і трасування повільних запитів наступним кроком.
Завдання 2: Швидко перевірити насиченість PHP-FPM пулу
cr0x@server:~$ sudo ss -s
Total: 1560 (kernel 0)
TCP: 932 (estab 210, closed 590, orphaned 0, timewait 560)
Transport Total IP IPv6
RAW 0 0 0
UDP 12 10 2
TCP 342 290 52
INET 354 300 54
FRAG 0 0 0
Що це означає: Велика кількість timewait і багато TCP-чорги може натякати на шторм підключень (або PHP↔DB, або клієнт↔веб), але це ще не доказ.
Рішення: Перевірте стан PHP-FPM і slowlog, потім — потоки БД.
Завдання 3: Перевірити тиск процесів PHP-FPM
cr0x@server:~$ ps -o pid,etime,pcpu,pmem,cmd -C php-fpm8.2 --sort=-pcpu | head
PID ELAPSED %CPU %MEM CMD
2261 01:12:08 18.3 1.6 php-fpm: pool www
2377 00:45:12 16.9 1.5 php-fpm: pool www
2410 00:21:33 15.2 1.5 php-fpm: pool www
2011 02:10:40 0.3 0.6 php-fpm: master process (/etc/php/8.2/fpm/php-fpm.conf)
Що це означає: Воркери зайняті тривалий час. Це часто означає, що вони заблоковані — на БД, файловій системі, зовнішніх HTTP викликах або на важких PHP-операціях.
Рішення: Увімкніть/перевірте PHP-FPM slowlog або сторінку статусу, щоб визначити, де витрачається час.
Завдання 4: Знайти «server reached pm.max_children» у логах PHP-FPM
cr0x@server:~$ sudo grep -R "max_children" -n /var/log/php8.2-fpm.log | tail -n 5
[29-Dec-2025 10:11:44] WARNING: [pool www] server reached pm.max_children setting (40), consider raising it
Що це означає: Запити ставлять в чергу на PHP-FPM. Підвищення max_children може допомогти, або ж лише посилить тиск на БД і погіршить інцидент.
Рішення: Якщо БД уже насичена, не підвищуйте цей параметр навмання. Спочатку підтвердьте наявний запас по БД.
Завдання 5: Перевірити ідентифікацію та версію БД (не вгадуйте)
cr0x@server:~$ mysql -NBe "SELECT VERSION(), @@version_comment;"
10.6.18-MariaDB-0ubuntu0.22.04.1 Ubuntu 22.04
Що це означає: У вас MariaDB 10.6, а не Oracle MySQL. Це впливає на доступні змінні, поведінку Performance Schema і очікування інструментів.
Рішення: Використовуйте налаштування й метрики, орієнтовані на конкретний движок. Не копіюйте поради, що стосуються тільки MySQL 8, у MariaDB.
Завдання 6: Перевірити кількість підключень і розподіл станів потоків
cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Threads_%';"
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_cached | 18 |
| Threads_connected | 420 |
| Threads_created | 9812 |
| Threads_running | 64 |
+-------------------+-------+
Що це означає: 420 підключень і 64 виконувані потоки. Threads_created високе, що свідчить про чурн або недостатній thread cache (або шторм підключень). Під час сплесків це може перетворитися на боротьбу планувальника.
Рішення: Якщо підключення масштабуються лінійно з трафіком, пріоритезуйте пулювання/обмеження підключень (ProxySQL, обережні persistent connections або менше PHP-воркерів).
Завдання 7: Визначити найтоповіші виконувані запити зараз
cr0x@server:~$ mysql -e "SHOW FULL PROCESSLIST;" | head -n 15
Id User Host db Command Time State Info
1283 wp wpapp:42110 wpdb Query 12 Sending data SELECT option_name, option_value FROM wp_options WHERE autoload = 'yes'
1290 wp wpapp:42118 wpdb Query 11 Waiting for table metadata lock ALTER TABLE wp_posts ADD INDEX idx_post_date (post_date)
1299 wp wpapp:42130 wpdb Query 10 Locked UPDATE wp_options SET option_value='...' WHERE option_name='_transient_timeout_x'
Що це означає: Маємо ALTER TABLE, що чекає на metadata lock, і UPDATE, що заблокований. Тим часом багато запитів навантажують wp_options.
Рішення: Припиніть виконання DDL під час піку (завершіть його, якщо безпечно), а потім усуньте патерни доступу до wp_options і проблему з autoload.
Завдання 8: Підтвердити контенцію метаданих (MySQL та MariaDB)
cr0x@server:~$ mysql -e "SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS FROM performance_schema.metadata_locks WHERE LOCK_STATUS='PENDING' LIMIT 10;"
+--------------+-------------+-----------+-------------+
| OBJECT_SCHEMA| OBJECT_NAME | LOCK_TYPE | LOCK_STATUS |
+--------------+-------------+-----------+-------------+
| wpdb | wp_posts | EXCLUSIVE | PENDING |
+--------------+-------------+-----------+-------------+
Що це означає: Деякий процес чекає на ексклюзивний metadata lock (типово DDL) і перебуває в очікуванні, часто блокуючи або будучи заблокованим довгими транзакціями.
Рішення: Знайдіть блокувальника (довга транзакція) і або дочекайтесь її завершення, або завершіть її. Перенесіть DDL на непіковий час і використовуйте інструменти для онлайн‑змін схеми за потреби.
Завдання 9: Виміряти тиск на InnoDB buffer pool
cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
+---------------------------------------+-----------+
| Variable_name | Value |
+---------------------------------------+-----------+
| Innodb_buffer_pool_read_requests | 986543210 |
| Innodb_buffer_pool_reads | 5432109 |
+---------------------------------------+-----------+
Що це означає: Innodb_buffer_pool_reads — це читання з диска. Якщо це швидко зростає під час сплесків, ви пропускаєте кеш і платите латентністю I/O за кожен запит.
Рішення: Збільшіть buffer pool (в межах доступної RAM), зменшіть робочий набір (прибрати autoload, оптимізувати індекси) або додайте шари кешування, щоб зменшити читання з БД.
Завдання 10: Перевірити сигнали тиску redo log / чекпоінтів
cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_os_log_written'; SHOW GLOBAL STATUS LIKE 'Innodb_log_waits';"
+------------------------+------------+
| Variable_name | Value |
+------------------------+------------+
| Innodb_os_log_written | 8123456789 |
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| Innodb_log_waits | 421 |
+------------------+-------+
Що це означає: Нульове або зростаюче значення Innodb_log_waits свідчить про те, що сесії чекають місця в redo логах/флашингу. Під час сплесків це може знизити швидкість записів і спричинити каскадні таймаути.
Рішення: Перегляньте розмір redo логів і можливості I/O; зменшіть write amplification (поведінка плагінів, churn transient), і переконайтесь, що латентність стореджу адекватна.
Завдання 11: Визначити очікування блокувань (транзакції, що блокують інших)
cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | sed -n '1,120p'
TRANSACTIONS
------------
Trx id counter 4892011
Purge done for trx's n:o < 4891900 undo n:o < 0 state: running
History list length 3212
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 4892007, ACTIVE 19 sec
2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1
MySQL thread id 1299, OS thread handle 140312351889152, query id 712381 wpapp 10.0.0.12 wpdb updating
UPDATE wp_options SET option_value='...' WHERE option_name='_transient_timeout_x'
Що це означає: Транзакція активна 19 секунд і оновлює wp_options. History list length вказує на відставання purge; довгі транзакції можуть блокувати purge і збільшувати тиск на undo.
Рішення: Знайдіть, що тримає довгі транзакції (пакетні роботи, бекапи, адмін-сторінки), виправте їх або вимкніть, і розгляньте можливість зменшення обсягу транзакцій в додатку (складно для WordPress, але плагіни можна виправити або видалити).
Завдання 12: Перевірити наявність автозавантажених опцій (гаряча точка WordPress)
cr0x@server:~$ mysql -NBe "SELECT COUNT(*) AS autoload_rows, ROUND(SUM(LENGTH(option_value))/1024/1024,2) AS autoload_mb FROM wp_options WHERE autoload='yes';"
4123 18.74
Що це означає: 18.74 MB автозавантажених опцій, що завантажуються часто. Це не «трохи накладних витрат». Це податок на кожен некешований запит.
Рішення: Зменшіть набір autoload (виправте плагіни, винесіть великі бінарні дані, встановіть autoload=no де безпечно). Це може бути ефективніше за заміну MySQL на MariaDB.
Завдання 13: Підтвердити латентність сховища (бо бази даних мають почуття)
cr0x@server:~$ iostat -xz 1 3
Linux 6.5.0 (server) 12/29/2025 _x86_64_ (8 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
18.22 0.00 9.31 14.70 0.00 57.77
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s wrqm/s %wrqm w_await wareq-sz aqu-sz %util
nvme0n1 180.0 8200.0 0.0 0.0 6.20 45.6 95.0 9100.0 5.0 5.0 18.40 95.8 2.10 92.0
Що це означає: w_await 18ms і завантаження пристрою 92%. Під час сплесків цього достатньо, щоб збільшити латентність запитів і створити черги.
Рішення: Або зменште навантаження на запис (кешування, налаштування флашингу, виправлення плагінів), або оновіть сховище / виділіть окремий диск для БД / забезпечте правильну конфігурацію RAID/NVMe.
Завдання 14: Тимчасово увімкнути лог повільних запитів (точково, не назавжди)
cr0x@server:~$ mysql -e "SET GLOBAL slow_query_log=ON; SET GLOBAL long_query_time=0.5; SET GLOBAL log_queries_not_using_indexes=ON;"
Що це означає: Ви зафіксуєте запити повільніші за 0.5 с та ті, що не використовують індекси. Це може створити шум; не лишайте це ввімкненим постійно при великому обсязі.
Рішення: Використовуйте його під час вікна інциденту, потім проаналізуйте топових порушників і вимкніть/знизьте налаштування.
Завдання 15: Побачити топ таблиць за читково-записувальним тиском (метрики InnoDB)
cr0x@server:~$ mysql -e "SELECT * FROM sys.schema_table_statistics ORDER BY rows_read DESC LIMIT 5;"
+----------------+------------+-----------+----------------+----------------+------------+------------+------------+-------------------+-------------------+
| table_schema | table_name | total_rows| rows_fetched | fetch_latency | rows_insert| rows_update| rows_delete| io_read_requests | io_write_requests |
+----------------+------------+-----------+----------------+----------------+------------+------------+------------+-------------------+-------------------+
| wpdb | wp_options | 52341 | 98122312 | 00:14:12.123456| 120 | 53210 | 8 | 842113 | 290112 |
| wpdb | wp_postmeta| 984122 | 55122311 | 00:12:40.654321| 210 | 1201 | 12 | 721223 | 120991 |
+----------------+------------+-----------+----------------+----------------+------------+------------+------------+-------------------+-------------------+
Що це означає: Звичні підозрювані. Якщо wp_options і wp_postmeta домінують, спочатку виправляйте патерни WordPress, перш ніж купувати більш залізний хост.
Рішення: Пріоритезуйте індексацію, гігієну autoload, object caching і аудит плагінів.
Звичні шаблони колапсу (і як вони виглядають)
Шаблон A: Шторм підключень і «трясіння» потоків
Це поширено, коли пост стає вірусним або ботнет «повзає» ваші динамічні endpoint-и. PHP-FPM масштабує воркери. Кожен воркер відкриває підключення до БД (або кілька). БД породжує потоки. Час CPU витрачається на переключення контексту, суперечку за м’ютекси і просто на управління надто великою кількістю сесій.
Що ви бачите:
- Threads_connected різко зростає.
- Threads_created швидко піднімається.
- CPU system time зростає, load average піднімається, але «корисної роботи» мало.
- Багато запитів короткі, але все чекає у черзі.
Хто ламається першим? Будь-хто. Але MariaDB з thread pool може бути більш граціозною тут, якщо налаштована, бо вона обмежує паралельне виконання і зменшує трясіння. MySQL може зрівнятися, якщо ви обмежите конкуренцію на рівні додатка або додасте проксі-пул.
Шаблон B: Гаряча точка wp_options + автозавантаження
Автозавантажені опції WordPress завантажуються дуже часто. Додайте кілька плагінів, що зберігають великі серіалізовані масиви з autoload=yes, і ви самі собі створите маленький DOS.
Що ви бачите:
SELECT option_name, option_value FROM wp_options WHERE autoload='yes'постійно з’являється.- Часті пропуски buffer pool (якщо робочий набір не вміщується в RAM).
- CPU зростає через парсинг запитів і обробку рядків.
Хто ламається першим? Той із меншим обсягом RAM, гіршими індексами або гіршою стратегією кешування. Вибір движка вас не врятує від зловживання autoload.
Шаблон C: Накопичення блокувань через записи і фонові задачі
Сплески трафіку часто супроводжуються більшою кількістю коментарів, логінів, оновлень кошиків (якщо WooCommerce) і оновлень transient. Записи створюють блокування. Довгі запити або «невинні» адмін-завдання можуть утримувати блокування й блокувати систему.
Що ви бачите:
- Стан processlist: Locked, Waiting for…
- InnoDB status показує довгі транзакції і зростаючу history list length.
- Таймаути очікування блокувань і deadlock-и.
Хто ламається першим? Знову ж — обидва. Різниця в оперативності: наскільки швидко ви можете побачити блокувальника і безпечно пом’якшити проблему без погіршення ситуації.
Шаблон D: Насичення I/O (тихий вбивця)
Коли дискова латентність зростає, все уповільнюється. База даних не «впадає». Вона просто стає підсилювачем латентності. PHP-воркери накопичуються. Nginx таймаутить. Ви отримуєте 504 і багато заплутаних думок.
Що ви бачите:
- iowait зростає.
- %util диска високий, await зростає.
- Innodb_buffer_pool_reads швидко зростає.
- Очікування чекпоінтів/логів.
Жарт №2: База даних не «впала». Вона просто взяла довгу, змістовну паузу, щоб поміркувати про ваші вибори сховища.
Три корпоративні міні-історії з реального життя
Міні-історія 1: Інцидент через хибні припущення
Компанія була в процесі міграції з більш старого MySQL на MariaDB, бо «це drop-in». Вони виконали розумні кроки: реплікували дані, репетирували cutover, перевіряли тести додатка. Вони зробили менш розумний крок: припустили, що їхні операційні інструменти поведуться однаково.
У день запуску трафік зріс — маркетинг зробив свою роботу. WordPress почав повертати 504. On-call виконали звичний план: витягнули топові запити з дашборда моніторингу, перевірили відставання реплікації та інспектували звичні лічильники стану MySQL.
Дашборд виглядав дивно спокійно. Підключення були «в нормі». Час запитів — «в нормі». Але Nginx таймаутив, а PHP-FPM кричав про max_children. Команда півгодини шукала проблему у веб-шарі, бо графіки бази даних мовчали.
Коренева проблема була проста й незручна: їхній експортер опирався на MySQL-специфічні таблиці Performance Schema і повертав неповні метрики на MariaDB. БД була насичена блокуваннями та дисковою латентністю, але графіки брехали шляхом опускання даних. Вони виправили це, розгорнувши експортер, сумісний із MariaDB, і додавши панелі з вибірками SHOW PROCESSLIST. 504 були проблемою бази даних; інцидент — проблемою спостережуваності.
Міні-історія 2: Оптимізація, що відбилася бумерангом
Інша організація мала сайт WordPress, який періодично бомбардували краулери. Хтось вирішив фіксити це, збільшивши конкуренцію скрізь: підвищили PHP-FPM max_children, max_connections MySQL і кількість веб-воркерів. Намір був благий: «обробляти більше трафіку». Ефект був більш схожий на «перенести більше страждань».
Наступний сплеск потрапив, і БД упала сильніше, ніж раніше. Не впала — гірше. Вона лишалась онлайн й відповідала повільно. CPU не був на 100%, але латентність пішла вертикально. Threads_created вибухнули. Ядро витрачало час на переключення контексту. Глибина черги сховища залишалась високою, бо записи приходили швидше, ніж їх можна було скинути. Nginx повертав 504, ніби отримував від цього прибуток.
Команда відкотила зміни конкурентності, і система стабілізувалась. Пізніше вони впровадили непривабливе, але правильне рішення: обмежили PHP-FPM до числа, яке БД могла витримати, додали connection pooler і поставили кешування перед найважчими endpoint-ами. Урок закарбувався: підняття лімітів збільшує радіус ураження. Це не збільшує ємність, якщо щось інше не змінилося.
Міні-історія 3: Сухо, але правильно — і це врятувало
Ця команда запускала WordPress з суворим календарем змін і звичкою, яку всі висміювали: вони репетирували incident response і тримали політику «peak freeze». Під час прогнозованих подій трафіку (запуски продукту, великі медіа) вони не деплоїли зміни схеми, плагінів або «швидких правок» у wp-admin.
Одного дня трафік потрійно зріс несподівано через соцмережі. Сайт сповільнився, але не впав. Nginx cache обслуговував більшість анонімного трафіку. PHP-FPM мав жорстку межу. БД мала запас, бо робочий набір вміщувався в buffer pool, а slow query log був відібраний і очищений тижні тому.
Вони все одно отримали алерти: реплікація трохи відстала, p95 латентності зросла. Але нічого не досягло таймаутів. Їхній канал інциденту залишився нудним — це найвища похвала для SRE-системи.
Рятівний крок не був магічним движком бази даних. Це була оперативна дисципліна: планування ємності, кешування, гігієна запитів і відмова від виконання DDL у найгірший час.
Типові помилки: симптом → коренева причина → виправлення
-
Симптом: стрибок 504, у логах PHP-FPM видно max_children reached, CPU БД виглядає «не дуже високим».
Коренева причина: шторм підключень і накладні витрати планувальника; збільшення латентності без явного піку CPU.
Виправлення: Обмежити PHP-FPM, додати connection pooling, зменшити max_connections, щоб створити зворотний тиск, і кешувати анонімний трафік. -
Симптом: багато запитів в стані «Waiting for table metadata lock».
Коренева причина: DDL під час піку, заблокований довгими транзакціями (або блокує інших).
Виправлення: Припинити DDL під час піку; знайти й завершити довгі транзакції; планувати онлайн‑зміни схеми у непіковий час. -
Симптом: стрибки корелюють з %util диска близько 100% і високим await; БД «повсюди повільна».
Коренева причина: насичення I/O і недостатній buffer pool; тиск redo/checkpoint.
Виправлення: Збільшити buffer pool, налаштувати розмір redo логів і флеш‑поведінку, зменшити write amplification (transients), оновити сховище або ізолювати диск для БД. -
Симптом: один тип сторінки (пошук, сторінки категорій) викликає таймаути, інші в порядку.
Коренева причина: конкретні повільні запити (часто join-и поwp_postmeta) без індексів або з filesort/тимчасовими таблицями.
Виправлення: Зловіть повільні запити, додайте цільові індекси або змініть патерни плагіна/теми. Не «оптимізуйте все». -
Симптом: після «переміщення на MariaDB/MySQL» все стало трохи гірше, не катастрофічно.
Коренева причина: зміни планів оптимізатора, відмінності в колаціях, або невідповідність налаштувань за замовчуванням; дашборди можуть бути неповними.
Виправлення: ПорівняйтеEXPLAINплани, перевірте критичні запити, стандартизуйтесь на колаціях/charset, оновіть експортери та пороги алертів. -
Симптом: Репліки в порядку, але первинний сервер помирає під час сплесків.
Коренева причина: гаряча точка записів на первинному (options/transients/sessions); репліки не допомагають записям.
Виправлення: Зменшити записи (object cache, відключити web wp-cron, налаштувати transient-поведінку), розглянути розділення навантажень і оптимізувати гарячі таблиці.
Контрольні списки / покроковий план
Під час інциденту (зупинити 504)
- Підтвердити джерело таймауту: Nginx upstream timeouts vs PHP-FPM vs БД.
- Заморозити зміни: жодних оновлень плагінів, змін схеми, жодних «швидких фіксів» у wp-admin.
- Зменшити динамічне навантаження:
- Увімкніть/продовжте Nginx microcache для анонімного трафіку, якщо він є.
- Обмежте швидкість зловмисних endpoint-ів (пошук, xmlrpc.php, wp-login.php) якщо це схильність.
- Вимкніть wp-cron через веб-запити; запустіть його через системний cron, якщо можливо.
- Обмежте конкуренцію: тримайте PHP-FPM воркери на числі, яке БД може обслуговувати з прийнятною латентністю.
- Знайдіть і вбийте блокувальників: довгі DDL, metadata locks, runaway запити.
- Зберіть докази: зразки processlist, фрагменти slow log, InnoDB status, iostat.
Після інциденту (зробити повторення складнішим)
- Впровадити connection pooling, якщо бачите churn потоків або високі підключення під час сплесків.
- Виправити autoload у wp_options: провести аудит і зменшити; видалити надмірні автозавантажувані бінари.
- Додати/перевірити індекси для топових повільних запитів. Не гадати; вимірювати.
- Правильно налаштувати InnoDB: buffer pool, redo логи, поведінка флашингу відповідно до сховища.
- Покращити спостережуваність: переконатися, що метрики відповідають движку, дашборди показують активні запити і очікування блокувань.
- Практикувати пік-гідність: заборона змін схеми під час прогнозованих піків; репетиції відкату.
Поширені питання
1) Чи швидша MariaDB за MySQL для WordPress?
Іноді, в специфічних сценаріях конкуренції — особливо якщо використовується thread pool у MariaDB. Але більші визначники — це кешування, якість запитів, розмір buffer pool і контроль штормів підключень.
2) Чи вирішить перемикання движка мої 504?
Рідко само по собі. Більшість 504 під час сплесків виникають через необмежену конкуренцію, гарячі таблиці (wp_options), повільні запити або насичення I/O. Перемикання движка без усунення цих проблем — це рух вбік з новими режимами відмов.
3) Яке одне найкраще виправлення для сплесків WordPress?
Кешуйте анонімний трафік агресивно і обмежте динамічну конкуренцію. Якщо край може обслужити 80–95% запитів без залучення PHP/БД, сплески стають нудними.
4) Чи варто підвищувати max_connections, щоб уникнути помилок «too many connections»?
Тільки якщо ви довели, що БД може витримати більшу конкуренцію. Інакше ви міняєте швидку відмову на повільну смерть: більше черг, більше блокувань, більше пам’ятевого тиску, більше 504.
5) Чи варто використовувати ProxySQL для WordPress?
Якщо у вас часті сплески і багато короткочасних підключень — так. Він може згладити шторм підключень, дозволити маршрутизацію на репліки і дати точку контролю. Також він додає складність; оперуйте ним як повноцінним шаром, а не як побічну іграшку.
6) Чи допомагають репліки при 504?
Допомагають, якщо читання — ваш вузький зв’язок і ви справді можете спрямувати читання на репліки без ризику консистентності. Вони не допомагають при гарячих записах, блокуваннях на первинному або повільних записах через диск.
7) Чому CPU виглядає нормальним, поки сайт таймаутить?
Бо латентність може домінувати через очікування: I/O wait, lock wait, scheduler thrash, флашинг. Ваш графік CPU — це підказка, а не оракул істини.
8) Що варто налаштувати в InnoDB першочергово для WordPress?
Розмір buffer pool (щоб читання потрапляли в RAM), розмір redo логів відповідно до write rate і адекватне флашування. Потім фокус на запитах/індексах і autoload bloat — зазвичай вони справжні злочинці.
9) Чи MySQL 8 безпечніший вибір для сумісності?
Якщо ваші інструменти та екосистема виходять із припущення MySQL 8 семантики — так, зазвичай простіше операційно. MariaDB сумісна в багатьох випадках, але не ідентична; ставтесь до неї як до окремого продукту і перевіряйте поведінку.
Наступні кроки, які ви можете зробити цього тижня
- Заміряйте сплеск: зафіксуйте зразки processlist, фрагменти slow query логів (коротко) і I/O статистику під час піку. Не налаштовуйте навмання.
- Контролюйте конкуренцію: встановіть обмеження PHP-FPM на основі ємності БД; розгляньте pooler, якщо підключення churn.
- Виправте гарячі точки WordPress: зменшіть розмір autoloaded options, проаудіть плагіни, що навантажують
wp_postmeta, додайте відсутні індекси. - Зробіть кешування обов’язковим: page cache на краї для анонімних користувачів, object cache для важких шляхів, що звертаються до БД.
- Обирайте движок за операціями: виберіть той, який ваша команда зможе коректно моніторити, безпечно оновлювати і швидко відновлювати. Продуктивність — це фіча; відновлюваність — це стиль життя.