WordPress повільний: знайдіть вузьке місце крок за кроком (сервер, плагіни чи база даних)

Було корисно?

«WordPress повільний» — це не діагноз. Це скарга, як «машина відчувається дивно». Іноді причина — плагін, що робить 400 запитів до бази на сторінку. Іноді сервер задихається через перевантаження дискових операцій. Іноді кеш працює чудово для анонімних користувачів, а залогінені користувачі повзають, ніби платять за кожен запит.

Цей посібник допоможе припинити гадання. Ми пройдемо стек зовні всередину — мережа, веб‑сервер, PHP‑FPM, база даних, сховище, плагіни — з командами, які можна виконати сьогодні, і з рішеннями, які можна прийняти на основі результатів.

Швидкий план діагностики

Якщо у вас є лише 20 хвилин до того, як хтось почне «просто пробувати випадкові плагіни», зробіть це. Мета — відповісти на одне питання: куди йде час? Друга мета — довести це доказами, а не відчуттями.

1) По‑перше: виміряйте TTFB і розділіть хіти кешу від промахів

  • Перевірте одну публічну сторінку неавторизовано. Потім сторінку для залогіненого. Порівняйте TTFB і загальний час.
  • Якщо публічні сторінки швидкі, а залогінені повільні — не лізьте в конфігурацію Nginx одразу. Зазвичай це PHP, DB або поведінка плагінів, що обходять кеші.

2) По‑друге: перевірте насичення хоста (CPU, тиск пам’яті, диск I/O)

  • Високий CPU при низькому iowait: занадто багато роботи PHP або забагато одночасних запитів.
  • Високий iowait: вузьке місце у сховищі або база даних робить багато випадкових I/O.
  • Активність swap: ви вже запізнилися; виправляйте тиск пам’яті перш за все.

3) По‑третє: перевірте черги PHP‑FPM і slow логи

  • Якщо FPM вичерпав воркерів або запити очікують у черзі, ви побачите повільний TTFB і «стає гірше під навантаженням».
  • Увімкніть PHP‑FPM slowlog тимчасово. Це найближче до зізнання.

4) По‑четверте: перевірте MySQL на повільні запити та блокування

  • Slow query log + processlist + InnoDB status скажуть, чи база даних — гальмо.
  • Якщо ви бачите багато «Sending data» або довгих запитів до wp_options — це WordPress‑проблема, а не ядро.

5) По‑п’яте: визначте, який плагін/тема/шлях коду дорогий

  • Використайте WP‑CLI, щоб швидко відключити підозрілі плагіни на стенді або в контрольованому вікні.
  • Корелюйте: шлях запиту → PHP стек‑трейс (slowlog) → відбитки запитів (slow query log).

Ось і все. П’ять кроків. Якщо ви їх виконаєте, перестанете сперечатися про «Apache vs Nginx» і почнете виправляти реальне вузьке місце.

Кілька фактів і історія (корисне, не для вікторин)

  • WordPress почався в 2003 році як форк b2/cafelog, створений для повільнішого вебу, де серверна генерація сторінок і кешування були нормою.
  • Кешування запитів MySQL колись було популярним трюком для WordPress, але його витіснили в сучасних версіях MySQL через виникнення заторів під навантаженням.
  • WP‑Cron — це не справжній cron; це механізм «запускати задачі при відвідуванні сторінки». При низькому трафіку він ненадійний; при високому — може бути шумним.
  • WooCommerce змінив культуру продуктивності WordPress: блог перетворився на транзакційну систему, де затримка — це дохід, і фонові задачі важливі.
  • PHP 7 був кардинальним покращенням продуктивності порівняно з PHP 5.x. Багато старих історій «WordPress повільний» фактично були «PHP був повільний».
  • Автозавантажені опції в wp_options завантажуються на кожен запит. Надмірний autoload може карати кожну сторінку, кешовану чи ні.
  • HTTP/2 зменшив проблему багатьох дрібних ресурсів, але він нічого не зробив для повільного TTFB на бекенді. Люди досі плутають «завантажується повільно» з «завантажується повільно через мережу».
  • Object caching (Redis/Memcached) допомагає, коли повторюються дорогі запити, але також може маскувати фундаментальну проблему з запитом до моменту, коли кеш почне інтенсивно скидатися.

Визначте «поваільно» числами, не відчуттями

Ви не зможете налаштувати те, що не вмієте описати. «Повільно» потребує щонайменше трьох чисел:

  • TTFB (Time To First Byte): час до першого байта відповіді плюс мережеві затримки. Тут проявляються PHP, DB і очікування upstream.
  • Загальний час завантаження: включає зображення, JS, CSS. Може бути «швидкий бекенд, важкий фронтенд». Різні проблеми.
  • Перцентилі під навантаженням: p50 — як зазвичай; p95 — коли клієнти починають йти; p99 — коли дзвонить інцидент‑канал.

Також вирішіть: ми дебагимо публічний анонімний трафік чи залогінених/адмінів? Кешування WordPress зазвичай орієнтоване на анонімні сторінки. Шляхи для залогінених обходять кеші і кожного разу б’ють повний стек.

Почніть зовні: ізолюйте TTFB, мережу, CDN і кеш

Перш ніж SSH‑ти кудись, доведіть, чи у вас проблема бекенду чи проблема доставки фронтенду. Найпростіша пастка — оптимізувати базу даних, коли CDN неправильно налаштовано і кожен запит — cache miss. Друга найпростіша — оптимізувати CDN, коли бекенд витрачає 4 секунди на генерацію HTML.

Реальність кешу

Якщо ви використовуєте CDN або реверс‑проксі (Cloudflare, Fastly, Varnish, Nginx cache), перше питання: чи я отримую cache hits? Дивіться заголовки кешу. Якщо їх немає — ви працюєте в сліпу. Додайте їх або налаштуйте. Кеш, який не видно, — це лише чутка.

Розрізніть «повільний перший байт» і «повільне завантаження»

Високий TTFB означає бекенд або upstream‑очікування. Повільне завантаження означає розмір payload або клієнтське уповільнення. Якщо TTFB = 3 с, а сторінка 200 KB — то справа не у Wi‑Fi користувача. Це ваша відповідальність.

Жарт #1: Якщо ваш TTFB вимірюється в секундах, вітаю — ви випадково виставили маленьку пакетну задачу як веб‑сайт.

Вузькі місця сервера: CPU, пам’ять, диск I/O і насичення

Коли WordPress стає повільним, сервер зазвичай підказує причину. Треба просто запитати у правильному порядку. Правило SRE: спочатку вимірюйте насичення, бо воно створює «випадкові» симптоми всюди.

CPU: коли PHP стає обігрівачем

Високий CPU при низькому iowait зазвичай означає зайнятість PHP: важкі плагіни, дорогі шаблони, забагато одночасних запитів або «thundering herd» після промахів кешу.

Але обережно: «CPU на 100%» на маленькій VM може просто означати, що ви запустили магазин на машині для персонального блогу. Оптимізм — це добре.

Тиск пам’яті: вбивця продуктивності з хорошим PR

Мала кількість вільної пам’яті сама по собі не є поганою; Linux використовує пам’ять для кешу. Поганий сигнал — це активність свопу або зростаюча кількість великих page fault. Якщо коробка свапиться, латентність стає стрибкоподібною. Якщо вона свапиться під навантаженням — сайт починає «випадково» тайм‑аутитись.

Дисковий I/O: iowait — запах, а не вогонь

Високий iowait означає, що CPU чекає на сховище. WordPress може створювати I/O‑біль через:

  • MySQL, що робить випадкові читання через відсутні індекси або малий buffer pool.
  • Багато дрібних читань PHP файлів на повільному мережевому сховищі.
  • Логування на абсурдних швидкостях (access логи, debug логи, slow логи на завантажених сайтах без ротації).
  • Резервні копії або антивіруси, що насичують диск.

Кеш ядра та файлів: швидко, доки ні

Linux page cache робить повторні читання швидкими — доки тиск пам’яті не змусить витіснення. Якщо продуктивність падає після деплою або очищення кешу й потім «поступово прогрівається», це може бути поведінка файлового кешу або поведінка application/object cache. Різні виправлення, схожі симптоми.

Веб‑сервер і PHP‑FPM: звичайні підозрювані

У сучасному WordPress‑стеку запит зазвичай проходить: Nginx/Apache → PHP‑FPM → WordPress → MySQL → назад. Вузьке місце часто проявляється як черга десь уздовж цього ланцюга.

Веб‑сервер: тримайте його простим

Nginx і Apache обидва можуть добре обслуговувати WordPress. Більшість проблем з «продуктивністю веб‑сервера» — не в виборі, а в неправильній конфігурації, відсутності кешування або надмірних витратах на TLS на слабкому обладнанні.

PHP‑FPM: де черги стають помітними користувачу

PHP‑FPM має простий режим відмови: занадто багато одночасних запитів, недостатньо воркерів, і кожен воркер зайнятий надто довго. Це створює чергу. Черга створює латентність. Далі LB починає повторювати запити. Далі ви чуєте нові нецензурні слова.

Slow log PHP‑FPM — ваш друг. Він вказує на шлях коду. Він теж припиняє суперечки. Якщо slow log показує, що 70% часу йде на виклик API плагіна — вам не потрібна філософська дебата про налаштування opcache.

OPcache: малоризиковий виграш

OPcache — не опція для production WordPress. Без нього ви постійно перекомпільовуєте PHP‑скрипти. Це не «динаміка». Це марнотратство. Переконайтесь, що він увімкнений, адекватно розмірений і не перезапускається постійно.

База даних: вузькі місця MySQL/MariaDB і патології запитів

WordPress використовує MySQL як робочу конячу силу, а іноді як орендованого мула. База часто є вузьким місцем, бо вона спільна, недостатньо забезпечена ресурсами або їй ставлять патологічні запити.

Класичні болючі місця бази даних WordPress

  • Роздутий autoload у wp_options: величезні автозавантажені опції змушують кожен запит тягнути валізу даних з БД у пам’ять PHP.
  • Неіндексовані meta‑запити: wp_postmeta і wp_usermeta можуть стати могилою запитів.
  • Запити WooCommerce по замовленнях: складні join‑и й пошуки по meta можуть знищити повільне сховище.
  • Блокування: довгі транзакції або ALTER можуть блокувати читання/запис і створювати спайки.

Slow query log: найкращий документ «чому повільно»

Увімкніть slow query logging з низьким порогом тимчасово (наприклад 0.5–1с), щоб зафіксувати найбільш кричущих порушників. Потім групуйте за fingerprint (формою запиту), а не за точним текстом. Повторений однаковий запит — реальна стаття витрат.

InnoDB buffer pool: важіль пам’яті

Якщо buffer pool занадто малий щодо робочого набору, MySQL стає генератором I/O. На виділених хостах DB buffer pool часто ставлять до ~60–75% RAM. На shared хостах треба бути обережним, інакше MySQL почне конкурувати з ОС і програє.

Цитата (парафразована думка): «Надія — не стратегія.» — часто цитують в операційній культурі, пов’язують з підходами reliability engineering.

Рівень WordPress і плагіни: доведіть винуватця

Плагіни — це код, який ви не писали, що працює з тими ж привілеями, що й ваш код. Ставтесь до них як до постачальників з shell‑доступом: припускайте, що вони колись зроблять щось дивне.

Дорожчі шаблони поведінки, які ви справді можете виявити

  • Забагато запитів на запит: особливо в адмінці та WooCommerce.
  • Зовнішні HTTP‑виклики під час завантаження сторінки: маркетингові пікселі, перевірки ліцензій, API‑виклики «phone home».
  • Некешований шаблонний код: цикли, що породжують N+1 запити.
  • Автозавантажений мотлох: збереження великих масивів в опціях з autoload=yes.
  • Пошук і фільтрація: погано індексований пошук, wildcard LIKE, meta‑запити без обмежень.

Не «оптимізуйте», додаючи ще плагінів

Плагіни кешування можуть допомогти, але їх складання в стопку — це хобі, а не інженерний план. Один page cache, один object cache, одна CDN‑стратегія. Обирайте свідомо. Спостерігайте. Ітерація.

Жарт #2: Кожного разу, коли ви встановлюєте «Ultimate Speed Booster Pro», повільний запит отримує крила.

WP‑Cron і фонові роботи: прихований податок

WP‑Cron запускає заплановані задачі при відвідуванні сайту. Це означає:

  • На низько‑трафікових сайтах задачі можуть не виконуватись вчасно.
  • На високому трафіку задачі можуть запускатись занадто часто, накладатися і викликати спайки CPU/DB.

Якщо сайт сповільнюється «кожні кілька хвилин», WP‑Cron — головний підозрюваний. Також під підозрою: резервні копії, імпорти, кеш‑вормери, оптимізація зображень, синхронізація розсилок.

Зріла практика — відключити тригер WP‑Cron на завантаженнях сторінок і запускати його через системний cron у контрольованому ритмі.

Практичні завдання (команди + що показує вивід + яке рішення прийняти)

Це перевірки для production. Виконуйте їх у вказаному порядку. Кожна підкаже, що робити далі. Оце і є суть.

Завдання 1: Виміряйте TTFB і загальний час від краю сервера

cr0x@server:~$ curl -s -o /dev/null -w "namelookup:%{time_namelookup}\nconnect:%{time_connect}\nstarttransfer:%{time_starttransfer}\ntotal:%{time_total}\n" https://example.com/
namelookup:0.004
connect:0.021
starttransfer:1.842
total:1.913

Значення: starttransfer — це по суті TTFB (плюс мережа). Тут ≈1.84с, отже бекенд повільний.

Рішення: Якщо starttransfer високий, але connect низький — перестаньте звинувачувати DNS/CDN. Перейдіть до перевірки сервера/PHP/DB.

Завдання 2: Порівняйте час анонімного та залогіненого доступу

cr0x@server:~$ curl -s -o /dev/null -w "TTFB:%{time_starttransfer} total:%{time_total}\n" https://example.com/wp-admin/
TTFB:3.912 total:4.105

Значення: wp-admin значно повільніший. Швидше за все кеші обходяться; PHP/DB роблять більше роботи.

Рішення: Пріоритезуйте інспекцію PHP‑FPM і DB; не витрачайте час на налаштування кешування статичних ресурсів.

Завдання 3: Швидко перевірте системне навантаження, CPU і iowait

cr0x@server:~$ uptime
 14:22:18 up 36 days,  2:03,  1 user,  load average: 8.72, 7.91, 6.88
cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.5.0 (server)  12/27/2025  _x86_64_  (4 CPU)

Average:     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %idle
Average:     all   62.11    0.00    7.42   18.93    0.00    0.37    0.00   11.17

Значення: load average високий відносно 4 CPU, і iowait ≈19%. Диск вносить свій вклад.

Рішення: Перевірте затримки диска і I/O‑патерни MySQL; не підвищуйте відразу PHP воркерів.

Завдання 4: Перевірте, чи залучено swap або тиск пам’яті

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:           8.0Gi       6.7Gi       220Mi       210Mi       1.1Gi       680Mi
Swap:          2.0Gi       1.2Gi       820Mi
cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 3  1 1249280 225384  9120 988224   10   24   612   430  510  890 62  7 12 19  0

Значення: swap використовується і відбувається постійний своп‑in/out (si/so). Це викликає стрибкоподібні латентності.

Рішення: Зменшіть тиск пам’яті: правильно підберіть розмір VM, зменшіть PHP‑FPM max children, налаштуйте MySQL пам’ять або додайте RAM перед мікрооптимізаціями.

Завдання 5: Визначте топ‑споживачів CPU і пам’яті

cr0x@server:~$ ps -eo pid,comm,%cpu,%mem --sort=-%cpu | head
 2841 php-fpm8.2        38.2  3.1
 2910 php-fpm8.2        34.9  3.0
 1732 mysqld            21.4 24.8
 1120 nginx              3.1  0.4

Значення: PHP‑FPM їсть CPU, MySQL важкий у пам’яті (може бути нормально) і значущий CPU‑споживач.

Рішення: Паралельно інспектуйте насичення PHP‑FPM і поведінку запитів MySQL.

Завдання 6: Перевірте затримки диска і глибину черги

cr0x@server:~$ iostat -x 1 3
Linux 6.5.0 (server)  12/27/2025  _x86_64_  (4 CPU)

Device            r/s     w/s   rMB/s   wMB/s  avgrq-sz avgqu-sz   await  r_await  w_await  %util
nvme0n1          52.0    38.0     3.1     5.7     175.2     4.21   28.40    21.10    37.20   94.8

Значення: await ≈28ms і %util ≈95% означають насичення диска. MySQL і читання PHP файлів страждатимуть.

Рішення: Зменшіть I/O: виправте повільні запити/індекси, збільшіть InnoDB buffer pool якщо є пам’ять, перенесіть DB на швидше сховище або відокремте DB на окремий хост.

Завдання 7: Перевірте статус пулу PHP‑FPM на предмет черг (якщо ввімкнено)

cr0x@server:~$ sudo curl -s http://127.0.0.1/status?full | sed -n '1,25p'
pool:                 www
process manager:      dynamic
start time:           27/Dec/2025:13:55:21 +0000
start since:          1602
accepted conn:        19482
listen queue:         37
max listen queue:     211
listen queue len:     128
idle processes:       0
active processes:     24
total processes:      24
max active processes: 24
max children reached: 19
slow requests:        83

Значення: listen queue ненульова та «max children reached» високий. Запити чекають на воркерів.

Рішення: Не піднімайте сліпо max children. Спочатку зменшіть вартість запиту (slowlog, DB) і підтвердьте наявність пам’яті; інакше ви просто посилите своп і зробите гірше.

Завдання 8: Увімкніть і прочитайте PHP‑FPM slowlog (тимчасово, прицільно)

cr0x@server:~$ sudo grep -nE 'slowlog|request_slowlog_timeout' /etc/php/8.2/fpm/pool.d/www.conf
261:request_slowlog_timeout = 2s
262:slowlog = /var/log/php8.2-fpm.slow.log
cr0x@server:~$ sudo tail -n 20 /var/log/php8.2-fpm.slow.log
[27-Dec-2025 14:20:11]  [pool www] pid 2910
script_filename = /var/www/html/index.php
[0x00007f2a8c1a3f60] wp_remote_get() /var/www/html/wp-includes/http.php:271
[0x00007f2a8c1a3df0] some_plugin_license_check() /var/www/html/wp-content/plugins/some-plugin/core.php:812

Значення: PHP блокує зовнішній HTTP‑виклик всередині плагіна.

Рішення: Виправте поведінку плагіна (відключіть, налаштуйте, кешуйте результати, переведіть в асинхронний режим). Збільшення CPU не виправить очікування на мережу.

Завдання 9: Підтвердіть, що OPcache увімкнено і не замалий

cr0x@server:~$ php -i | grep -E 'opcache.enable|opcache.memory_consumption|opcache.max_accelerated_files' | head -n 5
opcache.enable => On => On
opcache.memory_consumption => 256 => 256
opcache.max_accelerated_files => 20000 => 20000

Значення: OPcache увімкнений і розмір адекватний для великої кількості WP файлів.

Рішення: Якщо OPcache вимкнений — увімкніть. Якщо пам’ять замала — ви побачите часті скидання кешу і зайві витрати CPU.

Завдання 10: Перевірте поточні запити MySQL і симптоми блокувань

cr0x@server:~$ sudo mysql -e "SHOW FULL PROCESSLIST\G" | sed -n '1,60p'
*************************** 1. row ***************************
     Id: 2198
   User: wp
   Host: 127.0.0.1:49822
     db: wordpress
Command: Query
   Time: 12
  State: Sending data
   Info: SELECT option_name, option_value FROM wp_options WHERE autoload = 'yes'
*************************** 2. row ***************************
     Id: 2202
   User: wp
   Host: 127.0.0.1:49836
     db: wordpress
Command: Query
   Time: 9
  State: statistics
   Info: SELECT * FROM wp_postmeta WHERE meta_key = '_price' AND meta_value BETWEEN '10' AND '50'

Значення: Довгі запити. Запит autoload має бути швидким; якщо ні — wp_options роздуті або сервер I/O‑зв’язаний. Запит по postmeta — класична проблема.

Рішення: Увімкніть slow query log і перегляньте індекси/розмір autoload. Розгляньте специфічні індекси для WooCommerce та зменшення meta‑фільтрів.

Завдання 11: Увімкніть slow query logging (коротке вікно) і перегляньте його

cr0x@server:~$ sudo mysql -e "SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 0.5; SET GLOBAL log_output = 'FILE';"
cr0x@server:~$ sudo tail -n 20 /var/log/mysql/mysql-slow.log
# Time: 2025-12-27T14:24:02.118532Z
# User@Host: wp[wp] @ localhost []  Id: 2251
# Query_time: 1.734  Lock_time: 0.000 Rows_sent: 1  Rows_examined: 874221
SET timestamp=1766845442;
SELECT * FROM wp_postmeta WHERE meta_key = '_price' AND meta_value BETWEEN '10' AND '50';

Значення: Rows_examined величезне порівняно з повернутими рядками. Це відсутній індекс / неправильна схема запиту.

Рішення: Не починайте з тонкого налаштування буферів. Виправте шлях запитів: змініть поведінку плагіна, зменшіть фільтрацію по meta, додайте правильні індекси де безпечно або використайте спеціалізовані таблиці для пошуку.

Завдання 12: Перевірте ефективність InnoDB buffer pool

cr0x@server:~$ sudo mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
+---------------------------------------+-----------+
| Variable_name                         | Value     |
+---------------------------------------+-----------+
| Innodb_buffer_pool_read_requests      | 987654321 |
| Innodb_buffer_pool_reads              | 3456789   |
+---------------------------------------+-----------+

Значення: Buffer pool reads вказують на читання з диска; requests — логічні читання. Якщо reads високі відносно requests, ваш робочий набір не поміщається в пам’ять.

Рішення: Якщо є RAM‑запас — збільшіть innodb_buffer_pool_size. Якщо ні — зменшіть робочий набір (очистіть autoload, виправте запити, архівуйте старі дані).

Завдання 13: Знайдіть розмір автозавантажених опцій (класичний тихий вбивця)

cr0x@server:~$ sudo mysql -D wordpress -e "SELECT ROUND(SUM(LENGTH(option_value))/1024/1024,2) AS autoload_mb FROM wp_options WHERE autoload='yes';"
+------------+
| autoload_mb|
+------------+
| 18.47      |
+------------+

Значення: 18MB автозавантажених опцій — величезно; це підтягується в пам’ять часто і може уповільнювати кожен некешований запит.

Рішення: Визначте найбільші autoload опції і виправте джерело (налаштування плагіна, неправильне використання transients). Орієнтуйтесь на низькі одиничні МБ або менше.

Завдання 14: Знайдіть найбільші автозавантажені опції (щоб знати, на кого кричати)

cr0x@server:~$ sudo mysql -D wordpress -e "SELECT option_name, autoload, ROUND(LENGTH(option_value)/1024,1) AS kb FROM wp_options WHERE autoload='yes' ORDER BY LENGTH(option_value) DESC LIMIT 10;"
+-------------------------------+----------+------+
| option_name                   | autoload | kb   |
+-------------------------------+----------+------+
| some_plugin_big_settings      | yes      | 5120 |
| rewrite_rules                 | yes      | 980  |
| another_plugin_cache_blob     | yes      | 740  |
+-------------------------------+----------+------+

Значення: Плагін автозавантажує багатомегабайтні бінари. Це не «налаштування», це крик про допомогу.

Рішення: Переконфігуруйте/замініть плагін або змініть autoload на no для конкретних опцій (обережно, після валідації). Також скиньте і перебудуйте rewrite rules, якщо цей запис роздутий.

Завдання 15: Перевірте, чи WP‑Cron не грібе запити

cr0x@server:~$ wp cron event list --fields=hook,next_run,recurrence --format=table
+------------------------------+---------------------+------------+
| hook                         | next_run            | recurrence |
+------------------------------+---------------------+------------+
| wp_version_check             | 2025-12-27 14:25:00 | twice_daily|
| some_plugin_sync_job         | 2025-12-27 14:23:00 | every_min  |
| woocommerce_cleanup_sessions | 2025-12-27 14:26:00 | hourly     |
+------------------------------+---------------------+------------+

Значення: «every_min» задачі можуть бути легітимними, але часто перекриваються і викликають періодичні спайки.

Рішення: Перенесіть cron у системний cron, зменшіть частоту і переконайтесь, що довгі задачі мають блокування, щоб уникнути накладання.

Завдання 16: Швидко перевірте вплив плагіна (стенд або контрольоване вікно)

cr0x@server:~$ wp plugin list --status=active --format=table
+---------------------+--------+-----------+---------+
| name                | status | update    | version |
+---------------------+--------+-----------+---------+
| woocommerce         | active | available | 8.4.0   |
| some-plugin         | active | none      | 3.2.1   |
| seo-suite           | active | none      | 19.0    |
+---------------------+--------+-----------+---------+
cr0x@server:~$ wp plugin deactivate some-plugin
Plugin 'some-plugin' deactivated.

Значення: Ви можете перевірити, чи дійсно винуватець зі slowlog спричиняє латентність.

Рішення: Якщо латентність значно зменшилася — тримайте плагін відключеним і плануйте заміну або виправлення від постачальника. Якщо нічого не змінилось — знову увімкніть і далі шукайте.

Три корпоративні міні‑історії (як це йде не так у житті)

Міні‑історія 1: Інцидент, спричинений неправильною припущенням

Компанія вела контентний WordPress‑сайт і окремий WooCommerce‑магазин. Під час кампанії контентний сайт почав тайм‑аутитись. Усі припустили, що проблема — CDN, бо «ми міняли правила кешу минулого тижня». Здавалося правдоподібним. Але це було неправильно.

Інженер on‑call зробив нудну річ: curl‑таймінги з кількох регіонів, SSH, iostat. TTFB був високий навіть всередині VPC. %util диска був зашпованим, iowait високий. MySQL стояв локально на тій же VM. CDN був невинний; він просто чесно доставляв повільні відповіді.

Неправильне припущення було в тому, що «статичні сторінки мають кешуватись, отже origin не може бути проблемою». Насправді редактори, що були залогінені, навантажували uncached ендпоїнти і викликали дорогі autoload‑завантаження опцій при кожному запиті. Кампанія збільшила адмін‑активність: більше чернеток, ревізій, попередніх переглядів і API‑викликів плагінів.

Виправлення не було героїчним: зменшили autoload‑роздування, відокремили базу на швидший інстанс і додали спостережуваність для hit‑rate кешу та глибини черг PHP‑FPM. Інцидент закінчився, коли перестали сперечатися про edge і почали вимірювати origin.

Міні‑історія 2: Оптимізація, що відкотилась назад

Ще одна команда мала повільні сторінки оформлення замовлення і вирішила «вирішити це кешуванням». Додали шар кешу сторінок і максимально підняли TTL. Анонімні сторінки продуктів виглядали чудово. Графіки в адмінці покращились. Люди святкували.

Потім кількість скарг від підтримки різко зросла. Покупці бачили застарілий інвентар і неконсистентні ціни. Залогінені користувачі залишались повільними, і тепер система була складніша в розумінні, бо кеші маскували бекенд‑проблеми. Page cache також помилково кешував персоналізовані фрагменти. Нічого кращого, ніж випустити поліпшення продуктивності, яке також випускає плутанину.

Коли вони нарешті включили PHP‑FPM slowlog, виявився плагін, що синхронно викликає зовнішні API для податків/доставки під час оформлення замовлення, без нормальних таймаутів і без кешування результатів. «Оптимізація» не зачепила цей шлях; вона просто зробила деякі графіки зеленішими.

Вони відкотили агресивне кешування, ввели розумні таймаути, додали асинхронні fallback‑механізми там, де бізнес дозволяв, і кешували відповіді API з ретельними ключами. Продуктивність покращилась, коректність повернулась, і всі вивчили ту саму науку: кешування не замінює розуміння.

Міні‑історія 3: Нудна, але правильна практика, що врятувала день

Медіа‑організація вела WordPress при стабільно великому трафіку. Нічого гламурного: новинні цикли, багато читачів, час від часу сплески. Їхня секретна зброя не була магічним плагіном. Це була дисципліна.

Вони вели slow query logging з розумним порогом, ротували і відправляли логи, і щотижня переглядали топ‑відбитки запитів. Не під час інцидентів. Щотижня. Вони також відстежували «max children reached» PHP‑FPM і hit rate buffer pool MySQL як стандартні метрики здоров’я.

Одної п’ятниці латентність почала поволі зростати. Ще не аварія — просто p95 піднімався. Оскільки були базові лінії, on‑call негайно помітив зростання buffer pool reads і підняття disk await. Це корелювалося з релізом нової фічі в плагіні, що додав мета‑запит по висококардинальних даних.

Вони відкотили зміну, додали індекс у staging, перевірили план запиту і знову задеплоїли. Користувачі майже не помітили. Команда пішла додому. Наблюваність не зробила їх швидшими на клавіатурі; вона зробила їх менш здивованими.

Поширені помилки: симптом → корінь → виправлення

1) Симптом: головна сторінка швидка, wp-admin надзвичайно повільний

Корінь: кеш сторінок допомагає анонімним користувачам; адмін обходить кеш і створює важкі DB‑запити, зовнішні виклики або autoload‑роздування.

Виправлення: увімкніть PHP‑FPM slowlog; перевірте розмір autoload; проведіть аудит плагінів, що чіпляються в адмін‑екрани; додайте object cache; зменшіть autoload у wp_options.

2) Симптом: продуктивність нормальна до сплеску трафіку, потім все тайм‑аути

Корінь: насичення і чергування: PHP‑FPM max children досягнуто, ліміти підключень BD, або диск I/O зашпований.

Виправлення: виміряйте глибину черг (FPM status), обережно підвищуйте потужність (CPU/RAM/швидший диск), додавайте кеш, зменшуйте роботу на запит.

3) Симптом: періодична повільність кожні кілька хвилин

Корінь: WP‑Cron або планові задачі, що накладаються, резервні копії, неправильна ротація логів або зовнішні синки.

Виправлення: перенесіть WP‑Cron у системний cron; додайте блокування; плануйте важкі задачі у непіковий час; моніторьте тривалість задач.

4) Симптом: CPU низький, але сторінки все одно довго починають завантажуватись

Корінь: блокуючий I/O: повільний диск, повільний DNS у зовнішніх викликах, зовнішні API або очікування в DB.

Виправлення: перевірте iowait/iostat; проаналізуйте PHP slowlog на wp_remote_* виклики; додайте таймаути і кешування для зовнішніх викликів.

5) Симптом: після встановлення плагіна TTFB подвоївся

Корінь: плагін додає дорогі хуки, автозавантажені опції або важкі meta‑запити на кожен запит.

Виправлення: відключіть і підтвердіть; інспектуйте slowlog і slow query log; замініть плагін або змініть конфіг; очистіть autoload.

6) Симптом: CPU бази даних високий, багато «Sending data» запитів

Корінь: неіндексовані запити, великі повні сканування таблиць або високе Rows_examined в slow log. Часто спричиняється функціями пошуку/фільтрації.

Виправлення: перегляньте відбитки slow query; обережно додайте/підкоригуйте індекси; зменшіть використання meta‑запитів; розгляньте зміну моделі даних для атрибутів продуктів.

7) Симптом: після перезапуску швидко, а потім повільно

Корінь: витоки пам’яті або ріст кешу (object cache, фрагментація opcache), або витіснення buffer pool через ріст робочого набору.

Виправлення: відстежуйте пам’ять з часом; перевірте налаштування OPcache; переконайтесь, що InnoDB buffer pool адекватний; лімітуйте кеші і очищуйте їх інтелектуально.

Контрольні списки / покроковий план

Контрольний список A: 30‑хвилинна діагностика (безпечна для production)

  1. Виміряйте TTFB і загальний час для однієї публічної URL і однієї admin URL.
  2. Перевірте насичення хоста: uptime, mpstat, vmstat, iostat.
  3. Перегляньте статус PHP‑FPM: довжина черги, max children reached, slow requests.
  4. Перевірте MySQL processlist на довгі запити; за потреби зберіть InnoDB status.
  5. Якщо диск зашпований: віддайте пріоритет виправленню DB‑запитів і апгрейду сховища над «більше PHP воркерів».
  6. Якщо CPU зайнятий PHP: увімкніть slowlog і знайдіть шлях коду.
  7. Запишіть: що змінилося нещодавно (оновлення плагінів, зміни теми, ріст DB, зсув трафіку).

Контрольний список B: план стабілізації на 1–2 дні

  1. Увімкніть slow query log (поріг 0.5–1с) в пікові вікна; збирайте топ‑відбитки запитів.
  2. Увімкніть PHP‑FPM slowlog на 2с тимчасово; ідентифікуйте повільні стек‑трейси.
  3. Аудит розміру autoload у wp_options; агресивно, але безпечно зменшіть його.
  4. Перенесіть WP‑Cron у системний cron; запобігайте накладанням задач.
  5. Підтвердіть OPcache і відповідність кількості PHP‑FPM воркерів реаліям RAM.
  6. Додайте object cache (Redis), якщо є повторювані дорогі запити і стабільні хіти кешу.
  7. Тестуйте зміни на стенді з production‑подібним обсягом даних. «Працює на чистій установці» — не тест продуктивності.

Контрольний список C: структурні покращення (те, що закінчить повторювані інциденти)

  1. Відокремте DB від веб‑шару, якщо конкуренція за ресурси реальна і постійна.
  2. Розмістіть DB на швидкому, низьколатентному сховищі; уникайте повільних мережевих дисків для write‑heavy навантажень.
  3. Інструментуйте hit/miss кешу, чергу PHP‑FPM та затримку DB як первинні метрики.
  4. Встановіть політику плагінів: власники, частота оновлень, план відкату і бюджет продуктивності.
  5. Регулярно обрізайте ревізії, transients, сесії і «сирі» meta залежно від бізнес‑потреб.
  6. Навантажте тестами великі релізи з реалістичною конкуренцією і потоками залогінених користувачів, а не лише анонімними хітами на домашню сторінку.

Питання й відповіді

1) Як дізнатися, чи вузьке місце — сервер чи база даних?

Дивіться на iowait, disk await і slow queries MySQL. Якщо iostat показує високий await/%util і slow log має високий Rows_examined — комбінація DB+диск, ймовірно, гальмує.

2) У мене низький CPU, але WordPress все одно повільний. Чому?

Очікування не спалюють CPU. Дискові очікування, мережеві очікування (зовнішні API) і блокування можуть створювати високий TTFB при низькому CPU. PHP‑FPM slowlog часто виявляє блокуючі виклики.

3) Чи слід піднімати PHP‑FPM max_children, щоб виправити повільність?

Тільки якщо у вас є запас RAM і ваші запити вже ефективні. Якщо ви свапитесь або прив’язаний до диска, більше воркерів підвищить конкуренцію і погіршить латентність.

4) Чи Redis автоматично робить WordPress швидким?

Ні. Redis допомагає, коли повторюються дорогі пошуки і їх можна безпечно кешувати. Він не виправить неіндексовані унікальні запити або плагіни, що роблять зовнішні виклики.

5) Чому wp-admin повільніший за фронтенд?

Адмінські екрани зазвичай обходять кеш сторінок, виконують більше запитів, підключають більше коду і активують плагінні хуки. Також cookies залогінених часто заважають CDN‑кешуванню за дизайном.

6) Який найшвидший спосіб знайти поганий плагін?

PHP‑FPM slowlog для стек‑трейсів плюс WP‑CLI, щоб вибірково вимикати плагіни (краще на стенді). Корелюйте з slow query log. Не спирайтесь лише на «здається, почалося після…» без доказів.

7) Чи WooCommerce априорі повільний?

Не априорі, але його легко зробити повільним, бо багато структурних даних зберігаються в meta‑таблицях і запускаються складні запити. Шляхи оформлення замовлення особливо чутливі до зовнішніх інтеграцій.

8) Чому речі періодично сповільнюються, як за годинником?

WP‑Cron, резервні копії, logrotate, імпорт/синхронізації продуктів або очищення кешу. Корелюйте з логами cron і системною активністю (піки iostat, піки MySQL) у ті моменти.

9) Якщо я перехожу на більший сервер, чи вирішить це все?

Це може дати час, і іноді це правильне рішення. Але якщо у вас патологічний запит, що сканує мільйони рядків, проблема повернеться при рості даних — зазвичай у найгірший момент.

10) Користуватись Nginx або Apache для продуктивності?

Обирайте той, яким ви вмієте керувати. Більшість повільностей WordPress — upstream (PHP/DB/плагіни) або стратегія кешування. Вибір веб‑сервера рідко є реальним вузьким місцем.

Наступні кроки (що робити в понеділок вранці)

  1. Виміряйте TTFB для публічних і адмінних шляхів і запишіть числа. Якщо не можете кількісно виміряти — не зможете покращити.
  2. Перевірте насичення перш за все: CPU, тиск пам’яті, затримки диска. Виправте своп і диск‑пеггінг перед тим, як налаштовувати плагіни.
  3. Увімкніть двох «правдорубів» (тимчасово, обережно): PHP‑FPM slowlog і MySQL slow query log.
  4. Виправляйте великі проблеми: autoload‑роздування, відбитки повільних запитів, синхронні зовнішні виклики і хаос WP‑Cron.
  5. Зробіть це видимим: черга FPM, hit‑rate кешу, затримка DB. Якщо ви не бачите — будете переживати це знову.

Продуктивність WordPress не містична. Вона просто нашарована. Знімайте цибулю у правильному порядку, і ви знайдете вузьке місце без жертвування вихідних через вірування.

← Попередня
Proxmox 2FA: як уникнути блокування й швидко відновити доступ
Наступна →
Чому синтетичні бенчмарки брешуть (і як їх викрити)

Залишити коментар