PostgreSQL проти Percona Server: відлагодження уповільнень — у кого краща видимість

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

Тривога спрацювала. Додаток «працює», але все ніби тоне в патоці меду. Латентність API стрибає, черги ростуть, а ваш генеральний директор дізнається про кнопку «оновити».
У вас дві проблеми: уповільнення і невизначеність. Друга гірша.

PostgreSQL і Percona Server (сумісний з MySQL) обидва можуть працювати швидко в масштабі. Різниця під час інциденту — як швидко вони дозволяють довести, що насправді відбувається:
CPU, I/O, блокування, тиск чекпойнтів, промахи буферного пулу, борг autovacuum, зупинки реплікації, погані плани або «хтось змінив одну крихітну річ».

Основна думка: видимість — це функція продукту

Якщо вам важлива реакція на інциденти, PostgreSQL загалом дає більше «пояснювальної правди» з коробки: wait events, міцну пер-запитну статистику з
pg_stat_statements і культуру висвітлення внутрішнього стану простим SQL. Ви швидко зможете дійти до «ми заблоковані через блокування» або «ми прив’язані до
I/O при читанні».

Суперсила Percona Server у тому, що він бере історію інструментації MySQL (яка може бути чудовою, але іноді потребує налаштування) і робить її більш операційною:
додаткові метрики, кращі налаштування за замовчуванням для спостережуваності та відпрацьований інструментарій (Performance Schema, sys schema, slow log, pt-query-digest),
який жорстко ефективний у продакшні.

Отже, хто дає кращу видимість? На практиці:

  • PostgreSQL виграє, коли вам потрібна єдина історія по виконанню запитів, очікуванням, блокуванням, vacuum-ам і поведінці планів — особливо при доступі тільки через SQL.
  • Percona Server виграє, коли ви хочете багате, оперативно налаштоване середовище MySQL, покращену інструментацію і перевірені робочі процеси «візьми slow log, пропусти через digest, виправ найгірші».

Неприємна правда: нічого з цього не допоможе, якщо ви не ввімкнули потрібні перемикачі до інциденту. Видимість — це не настрій. Це файл конфігурації і політика збереження.

Цікаві факти та історичний контекст (8 речей, що важливі)

  1. Походження PostgreSQL — спочатку дослідження. Він походить від POSTGRES (1980-ті), і це видно: внутрішній стан часто виставляється в таблицях/видах, які ніби зроблені для дебагу.
  2. Ранній профіль продуктивності MySQL базувався на простоті. Протягом років «він швидкий» іноді означало «він непрозорий», і екосистема компенсувала це логами та зовнішніми інструментами.
  3. Performance Schema став переламним моментом. Performance Schema MySQL еволюціонував у повноцінний фреймворк інструментації; він потужний, але не завжди ввімкнений/правильно налаштований за замовчуванням.
  4. Percona популяризував «операційний MySQL». Percona Server і його інструменти зробили діагностику продакшн-проблем MySQL менш мистецькою і більш чеклістовою.
  5. pg_stat_statements змінив спосіб роботи команд Postgres. Коли у вас є нормалізована статистика запитів, «топ запитів» стає даними, а не суперечкою.
  6. Wait events у PostgreSQL — першокласні. Звіти про wait events (і пов’язані view) дають чітку історію «на що ми чекаємо».
  7. Буферний пул InnoDB — і благословення, і пастка. Коли його розмір правильний, усе виглядає відмінно. Коли ні — ви годинами ганятимете «таємничі I/O».
  8. Autovacuum — збирач податків PostgreSQL. Його можна ігнорувати деякий час, але він стягне свої борги — з відсотками — через блоут, повні скани та жорсткі стрибки латентності.

Що означає «краща видимість» в реальних інцидентах

Видимість — це не «у мене є дашборд». Видимість — це здатність швидко відповісти на чотири питання з доказами:

  • Що змінилося? Форма запиту, обсяг даних, план, конфіг, схема, змішування навантаження, поведінка хоста, мережа.
  • Куди йде час? CPU, читання диска, запис диска, очікування блокувань, fsync, реплікація, чекпойнти, vacuum/purge, конкуренція.
  • Хто відповідальний? Які шаблони запитів, які користувачі, які таблиці/індекси, який шлях коду, яка задача.
  • Яке найбезпечніше пом’якшення? Вбити, обмежити, додати індекс, змінити план, налаштувати пам’ять, знизити надійність записів, тимчасово призупинити завдання, переключити фейловер.

PostgreSQL і Percona Server обидва можуть відповісти на ці питання. Реальна різниця — скільки кроків це займає і як часто ви змушені робити вигуки.

Дві метрики важать більше за ваше самолюбство:

  • Час до першої достовірної гіпотези. Не «це база», а «це конкуренція блокувань на таблиці X через шаблон запитів Y».
  • Час до безпечного пом’якшення. Виправлення не має бути досконалим; воно має зупинити кровотечу, не пошкодивши дані й не спричинити другого відключення.

PostgreSQL: де світло яскраве (і де його бракує)

Сильні сторони Postgres: SQL-нативна інспекція і правдиві wait events

PostgreSQL виставляє багато того, «що робить рушій», через системні view. В інциденті це означає, що ви часто можете залишатися всередині SQL і все одно отримати відповіді:
сесії, блокування, wait events, текст запиту, таймінги I/O (якщо ввімкнено), прогрес vacuum, реплікацію та статистику запитів.

Wait events — недооцінена фіча. Якщо сесії застрягли на очікуваннях типу Lock, ви негайно переходите від «чому повільно» до «хто кого блокує».
Якщо wait показує IO або тиск LWLock, ви знаєте, чи то диск, перемішування кешу, чи внутрішня конкуренція.

Сліпі плями Postgres: потрібно попередньо ввімкнути корисне

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

  • pg_stat_statements треба встановити і розмірити. Якщо занадто мало місця, найгарячіші запити витіснять докази.
  • Інструментація таймінгів I/O коштує ресурсів. Ви можете її ввімкнути, але не прикидайтеся, що вона безкоштовна на сильозавантажених дисках.
  • Розбивка очікувань на рівні запиту не завжди настільки деталізована, як хотілося б. Ви дізнаєтеся, на що бекенд чекає, але не завжди отримаєте ідеальну пер-запитну атрибуцію без додаткових інструментів.

Практичний досвід дебагу в Postgres

На виклику з Postgres часто відчуття таке: «Запусти кілька SQL-запитів, і рушій все видасть». Це не завжди правда, але достатньо часто, щоб команди відточили м’язову пам’ять навколо цього.

Найпоширеніший режим відмови — не відсутність видимості, а її перенасичення. Якщо у вас немає робочого процесу, ви закінчите, дивлячись на
pg_stat_activity, ніби він має моргнути двічі, щоби сказати «диск заповнений».

Percona Server: де він блищить (і його сліпі плями)

Сильні сторони Percona: екосистема MySQL, розширені метрики та інструменти інцидентів

Percona Server сумісний з MySQL, але налаштований для продакшну: кращі налаштування інструментації за замовчуванням, додаткові змінні стану і культура «ось ручка, ось метрика, ось спосіб».
Робочий процес Percona toolkit — slow log + digest — залишається одним з найшвидших шляхів від «повільно» до «ось відбитки найгірших запитів і їхній вплив».

Performance Schema може бути дивовижним при правильній конфігурації: підсумки заяв, wait events, інструментація блокувань, стадій та metadata locks.
Поєднайте це з sys schema views — і отримаєте наратив, близький до wait story Postgres — тільки з більшим числом підводних каменів.

Сліпі плями Percona: інструментація опційна, а опціональні речі часто відсутні о 2-й ночі

Видимість MySQL/Percona історично залежала від того, чи ввімкнули ви її. Performance Schema може бути вимкнений, замалий за розміром або налаштований так, що не зберігає потрібне.
Slow логи можуть бути вимкнені через «мало місця на диску», і тоді ви дебагуєте на відчуттях і молитві.

Є також тонкість: MySQL має кілька способів спостерігати подібні речі (slow log, Performance Schema, INFORMATION_SCHEMA, змінні стану).
Це гнучкість — поки не стане проблемою. В інциденті ви хочете один канонічний робочий процес.

Жарт №1: Повільний запит схожий на корпоративну нараду — ніхто не знає, навіщо вона існує, але всі чекають на блокування.

Швидкий план діагностики (перевірити 1-ше/2-ге/3-тє)

Це той план, який я хочу повісити на стіну поруч із табличкою «не перезавантажувати базу».

Перше: це база чи сервер?

  • Перевірте насичення хоста: CPU, iowait, тиск пам’яті, свопінг, глибина черги диска.
  • Перевірте затримки сховища: read/write await, сплески fsync, завислі пристрої, заповнені файлові системи.
  • Перевірте мережу: повторні передачі, насичення, дивна латентність до клієнтів.

Рішення: якщо хост «горить», фіксуйте хост перш за все. Жодні налаштування запитів не переспівають диск із 80ms читаннями.

Друге: навантаження блокується (блокування) чи повільне через ресурси?

  • Postgres: wait events + view блокувань + pg_stat_activity.
  • Percona: Performance Schema waits + статус InnoDB lock/transaction + processlist.

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

Третє: знайдіть топ відбитків запитів, що спричиняють біль

  • Postgres: pg_stat_statements за загальним часом і середнім; корелюйте з кількістю виконань і I/O.
  • Percona: slow log + pt-query-digest; підсумки statement у Performance Schema.

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

Четверте: підтвердіть, що це не технічне обслуговування чи налаштування надійності

  • Postgres: частота чекпойнтів, активність autovacuum, відставання реплікації при застосуванні.
  • Percona: backlog purge/history list length, вік чекпойнта, затримки flush, відставання репліки.

Рішення: якщо винне обслуговування, обираєте між продуктивністю і боргом. Заплатіть зараз (vacuum/purge/optimize), або пізніше — з більшими відсотками.

Практичні завдання (команди, виводи, рішення)

Це реальні завдання, які ви можете виконати під час інциденту. Кожне містить: команду, що означає вивід, і рішення, яке ви приймаєте.
Я змішую перевірки на рівні хоста з перевірками на рівні БД, бо «база повільна» часто означає «диск сумний».

Завдання 1 — Снімок CPU та iowait хоста

cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.1.0 (db01)  12/30/2025  _x86_64_ (32 CPU)

11:02:10 AM  CPU   %usr  %nice   %sys %iowait  %irq  %soft  %steal  %guest  %gnice  %idle
11:02:11 AM  all  22.10   0.00   6.30   18.40  0.00   0.60    0.00    0.00    0.00  52.60
11:02:11 AM    7  85.00   0.00  10.00    0.00  0.00   0.00    0.00    0.00    0.00   5.00

Значення: високе %iowait на all вказує, що затримки сховища обмежують прогрес; одна „гаряча“ CPU свідчить про однонитковий гарячий шлях (сжаття, одиничний запит або фонова операція).

Рішення: якщо iowait високий, перемкніться на перевірки сховища перед тим, як переписувати запити.

Завдання 2 — Затримка диска і глибина черги

cr0x@server:~$ iostat -x 1 3
Linux 6.1.0 (db01)  12/30/2025  _x86_64_ (32 CPU)

Device            r/s     w/s   r_await   w_await   aqu-sz  %util
nvme0n1         820.0   210.0     18.40     22.10    14.20  98.50

Значення: r_await/w_await в десятках мс і %util близько 100%: пристрій насичений. Високе aqu-sz: черга глибока, запити накопичуються.

Рішення: обмежте навантаження (пул з’єднань, пакетні задачі), підтвердіть, що немає runaway чекпойнтів/флашів, розгляньте failover якщо репліка має здоровіше сховище.

Завдання 3 — Тиск пам’яті і свопінг

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:           128Gi       120Gi       1.2Gi       2.0Gi       6.8Gi       3.5Gi
Swap:           16Gi       9.5Gi       6.5Gi

Значення: використання swap на хості бази доволі рідко «нормальне». Зазвичай це означає, що ваш кеш виштовхується й латентність скоро стане інтерпретативним мистецтвом.

Рішення: зменшіть пам’ятний слід (знизьте кількість з’єднань, виправте надто великі work_mem/sort buffers, перевірте нестачу OS кешу) і плануйте перезапуск лише якщо це безпечно.

Завдання 4 — PostgreSQL: знайти активні запити та їхні очікування

cr0x@server:~$ psql -X -c "SELECT pid, usename, state, wait_event_type, wait_event, now()-query_start AS age, left(query,120) AS query FROM pg_stat_activity WHERE state <> 'idle' ORDER BY age DESC LIMIT 10;"
 pid  | usename | state  | wait_event_type |  wait_event   |   age    |                         query
------+--------+--------+-----------------+---------------+----------+----------------------------------------------------------
 4821 | app    | active | Lock            | relation      | 00:01:42 | UPDATE orders SET status=$1 WHERE id=$2
 4977 | app    | active | IO              | DataFileRead  | 00:01:10 | SELECT * FROM orders WHERE customer_id=$1 ORDER BY created

Значення: перша сесія чекає на relation lock; друга — на читання файлу даних. Це дві різні траєкторії інциденту: конкуренція блокувань vs обмеження I/O.

Рішення: якщо бачите Lock waits — досліджуйте блокери. Якщо IO waits — ідіть до перевірок диска і поведінки кешу.

Завдання 5 — PostgreSQL: показати блокерів і заблоковані сесії

cr0x@server:~$ psql -X -c "SELECT blocked.pid AS blocked_pid, blocked.query AS blocked_query, blocker.pid AS blocker_pid, blocker.query AS blocker_query FROM pg_locks bl JOIN pg_stat_activity blocked ON blocked.pid=bl.pid JOIN pg_locks kl ON kl.locktype=bl.locktype AND kl.database IS NOT DISTINCT FROM bl.database AND kl.relation IS NOT DISTINCT FROM bl.relation AND kl.page IS NOT DISTINCT FROM bl.page AND kl.tuple IS NOT DISTINCT FROM bl.tuple AND kl.virtualxid IS NOT DISTINCT FROM bl.virtualxid AND kl.transactionid IS NOT DISTINCT FROM bl.transactionid AND kl.classid IS NOT DISTINCT FROM bl.classid AND kl.objid IS NOT DISTINCT FROM bl.objid AND kl.objsubid IS NOT DISTINCT FROM bl.objsubid AND kl.pid != bl.pid JOIN pg_stat_activity blocker ON blocker.pid=kl.pid WHERE NOT bl.granted AND kl.granted LIMIT 5;"
 blocked_pid |             blocked_query              | blocker_pid |              blocker_query
------------+----------------------------------------+------------+------------------------------------------
       4821 | UPDATE orders SET status=$1 WHERE id=$2 |       4602 | ALTER TABLE orders ADD COLUMN note text

Значення: ALTER TABLE блокує продакшн-оновлення. Це не «повільний запит», це провал координації DDL.

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

Завдання 6 — PostgreSQL: топ запитів за загальним часом (потребує pg_stat_statements)

cr0x@server:~$ psql -X -c "SELECT calls, round(total_exec_time::numeric,1) AS total_ms, round(mean_exec_time::numeric,1) AS mean_ms, rows, left(query,120) AS query FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;"
 calls | total_ms | mean_ms |  rows  |                         query
-------+----------+---------+--------+----------------------------------------------------------
 92000 | 540000.0 |     5.9 | 120000 | SELECT * FROM orders WHERE customer_id=$1 ORDER BY created_at DESC LIMIT $2
  4100 | 210000.0 |    51.2 |   4100 | UPDATE orders SET status=$1 WHERE id=$2

Значення: перший запит домінує за загальним часом через обсяг. Другий індивідуально повільніший, але не обов’язково головний винуватець.

Рішення: оптимізуйте домінантний відбиток спочатку (індекс, покритий індекс, зменшити вибрані колонки, краща пагінація), потім працюйте з «хвостовою» латентністю.

Завдання 7 — PostgreSQL: перевірити борг autovacuum і сигнали ризику блоуту

cr0x@server:~$ psql -X -c "SELECT relname, n_dead_tup, n_live_tup, last_autovacuum, vacuum_count, autovacuum_count FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 5;"
  relname  | n_dead_tup | n_live_tup |     last_autovacuum     | vacuum_count | autovacuum_count
-----------+------------+------------+-------------------------+--------------+------------------
 orders    |   8420000  |   21000000 | 2025-12-29 02:14:11+00  |            1 |               14
 events    |   3200000  |    9000000 |                         |            0 |                0

Значення: багато dead tuples і відсутність autovacuum на events вказують на блоут і погане покриття visibility map, що означає більше I/O і повільніші скани.

Рішення: дослідіть налаштування autovacuum, довгі транзакції, що заважають очищенню, і заплануйте цілеспрямоване vacuum (обережно, у продакшні).

Завдання 8 — PostgreSQL: перевірити тиск чекпойнтів

cr0x@server:~$ psql -X -c "SELECT checkpoints_timed, checkpoints_req, round(checkpoint_write_time/1000.0,1) AS write_s, round(checkpoint_sync_time/1000.0,1) AS sync_s, buffers_checkpoint FROM pg_stat_bgwriter;"
 checkpoints_timed | checkpoints_req | write_s | sync_s | buffers_checkpoint
------------------+-----------------+---------+--------+-------------------
             1200 |            3400 |  9800.2 |  420.1 |          81233421

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

Рішення: налаштуйте параметри чекпойнтів (інтервал, completion_target) і перевірте, чи сховище витримує модель записів. Не «виправляйте» це, зневаживши цілісність.

Завдання 9 — Percona/MySQL: хто працює і хто чекає

cr0x@server:~$ mysql -e "SHOW FULL PROCESSLIST\G"
*************************** 1. row ***************************
     Id: 31244
   User: app
   Host: 10.0.12.34:52210
     db: prod
Command: Query
   Time: 87
  State: Waiting for table metadata lock
   Info: ALTER TABLE orders ADD COLUMN note TEXT
*************************** 2. row ***************************
     Id: 31251
   User: app
   Host: 10.0.12.18:50122
     db: prod
Command: Query
   Time: 83
  State: updating
   Info: UPDATE orders SET status='shipped' WHERE id=?

Значення: metadata locks можуть заморозити цілий додаток на MySQL/Percona. Якщо ви бачите «Waiting for table metadata lock», ви не оптимізуєте запити — ви закінчуєте перемовини.

Рішення: знайдіть утримувача блокування і вбийте або дочекайтеся; потім виправте процес міграції (online DDL інструменти, менші кроки, непіковий час).

Завдання 10 — Percona/MySQL: знімок транзакцій і блокувань InnoDB

cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | sed -n '1,120p'
=====================================
2025-12-30 11:04:21 INNODB MONITOR OUTPUT
=====================================
TRANSACTIONS
------------
Trx id counter 845112334
Purge done for trx's n:o < 845100000 undo n:o < 0 state: running
History list length 987654
...
LATEST DETECTED DEADLOCK
------------------------
*** (1) TRANSACTION:
TRANSACTION 845112100, ACTIVE 12 sec updating or deleting
...

Значення: дуже великий «History list length» вказує, що purge відстає, часто через довгі транзакції. Це збільшує утримання undo і може шкодити продуктивності.
Deadlock-и нормальні; повторювані шаблони — ні.

Рішення: знайдіть довгі транзакції (сплячі клієнти, пакетні задачі), виправте охоплення транзакцій в додатку і розгляньте налаштування purge/undo, якщо це хронічно.

Завдання 11 — Percona/MySQL: топ заяв через Performance Schema

cr0x@server:~$ mysql -e "SELECT DIGEST_TEXT, COUNT_STAR, ROUND(SUM_TIMER_WAIT/1000000000000,2) AS total_s, ROUND(AVG_TIMER_WAIT/1000000000000,4) AS avg_s FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 5\G"
*************************** 1. row ***************************
DIGEST_TEXT: SELECT * FROM orders WHERE customer_id = ? ORDER BY created_at DESC LIMIT ?
 COUNT_STAR: 92510
   total_s: 532.44
    avg_s: 0.0058

Значення: та сама ідея, що й у pg_stat_statements: нормалізовані відбитки з кількостями й часом. Якщо ця таблиця пуста, ймовірно, ви не налаштували збереження Performance Schema.

Рішення: оптимізуйте топ-диджест і переконайтесь, що споживачі Performance Schema ввімкнені і розмірені під ваше навантаження.

Завдання 12 — Percona/MySQL: slow log digest (швидке «хто нас вдарив»)

cr0x@server:~$ pt-query-digest /var/log/mysql/mysql-slow.log --limit=5
# 200ms user time, 20ms system time, 30.00M rss, 120.00M vsz
# Rank Query ID           Response time Calls R/Call  V/M   Item
# ==== ================== ============= ===== ======= ===== ====
#    1 0xA1B2C3D4E5F6A7B8  540.2311 50.2% 92000 0.0059  0.02  SELECT orders
#    2 0x1111222233334444  210.1145 19.5%  4100 0.0512  0.10  UPDATE orders

Значення: ви отримуєте миттєвий ранжований список того, що споживає латентність. Ось чому slow log ще актуальний у 2025 році: він працює під навантаженням.

Рішення: виправляйте позицію №1 першою, якщо ви не можете довести, що позиція №2 спричиняє ампліфікацію блокувань або каскадні відмови.

Завдання 13 — PostgreSQL: розглянути план з реальними таймінгами виконання

cr0x@server:~$ psql -X -c "EXPLAIN (ANALYZE, BUFFERS, VERBOSE) SELECT * FROM orders WHERE customer_id=42 ORDER BY created_at DESC LIMIT 50;"
 Limit  (cost=0.43..25.88 rows=50 width=312) (actual time=120.114..120.220 rows=50 loops=1)
   Buffers: shared hit=120 read=1840
   ->  Index Scan using orders_customer_created_idx on public.orders  (cost=0.43..5120.33 rows=10000 width=312) (actual time=0.080..119.900 rows=50 loops=1)
         Index Cond: (orders.customer_id = 42)
 Planning Time: 0.320 ms
 Execution Time: 120.300 ms

Значення: багато read буферів означає промахи кешу; індекс існує, але все одно спричиняє I/O (можливо через погану локальність, холодний кеш або дуже широкі рядки).

Рішення: розгляньте покритий індекс, зменште вибір колонок, покращіть шанс попадання в кеш (розмір пам’яті) або виправте затримки сховища.

Завдання 14 — Percona/MySQL: EXPLAIN з фактичними даними (якщо підтримується) або аналіз форми плану

cr0x@server:~$ mysql -e "EXPLAIN SELECT * FROM orders WHERE customer_id=42 ORDER BY created_at DESC LIMIT 50\G"
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: orders
   partitions: NULL
         type: ref
possible_keys: idx_customer_created
          key: idx_customer_created
      key_len: 8
          ref: const
         rows: 10000
     filtered: 100.00
        Extra: Using where; Using filesort

Значення: «Using filesort» з ORDER BY може означати, що індекс не задовольняє сортування або оптимізатор обрав інший шлях доступу. Класичний множник латентності.

Рішення: відрегулюйте порядок індексу, переконайтесь, що ORDER BY відповідає індексу, і перевірте статистику/кардинальність.

Завдання 15 — Перевірка відставання реплікації (Postgres)

cr0x@server:~$ psql -X -c "SELECT application_name, state, write_lag, flush_lag, replay_lag FROM pg_stat_replication;"
 application_name |   state   | write_lag | flush_lag | replay_lag
-----------------+-----------+-----------+-----------+------------
 replica01        | streaming | 00:00:00  | 00:00:02  | 00:00:15

Значення: replay lag вказує, що репліка застосовує WAL повільно. Під час відповіді на інцидент це важливо для безпеки фейловера і очікувань читання після запису.

Рішення: якщо відставання зростає, не переключайтеся на цю репліку, якщо ви не готові прийняти більше втрат/латентності. Дослідіть I/O/CPU на репліці.

Завдання 16 — Перевірка відставання реплікації (Percona/MySQL)

cr0x@server:~$ mysql -e "SHOW REPLICA STATUS\G" | egrep 'Replica_IO_Running|Replica_SQL_Running|Seconds_Behind_Source|Last_SQL_Error'
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 48
Last_SQL_Error:

Значення: 48 секунд відставання не обов’язково фатальні, але це попередження для фейловера і очікувань читання після запису.

Рішення: якщо відставання корелює з затримками сховища або блокуваннями, виправте основну проблему; не «вирішуйте» це перезапуском реплікації навмання.

Жарт №2: Нічого так не пришвидшує пошук кореневої причини, як усвідомлення, що «тимчасова» налагоджувальна опція увімкнена ще з минулого кварталу.

Три корпоративні міні-історії з передової

Міні-історія 1: Інцидент через хибне припущення

Середня компанія запускала мульти-арендний SaaS на PostgreSQL. У них була акуратна ментальна модель: «Читання дешеві, записи дорогі».
Тож коли вони побачили стрибки латентності, вони припустили, що фонове багато записів задач насичує диск.

На виклику відкрили метрики хоста: використання диска дійсно було високим. Але pg_stat_activity показав, що найгучніша проблема — це читальні запити.
Wait events не були всюди «IO DataFileRead» — багато сесій застрягли на Lock, і заблоковані запити були читаннями.

Хибне припущення було тонким: вони вірили, що читання не блокують. У PostgreSQL читання можуть чекати, якщо вони намагаються доступитися до об’єкта, заблокованого DDL,
або якщо їм потрібен буфер, притриманий іншим процесом, або якщо вони накопичуються за чергою блокувань. Міграція схеми додала індекс з режимом блокування, який вони не до кінця зрозуміли.

Фікс не був «додати IOPS». Фікс був операційним: змінити процес міграції на безпечні онлайн-патерни,
планувати інвазивні DDL на низькому трафіку і запровадити попередню перевірку, що перевіряє ризик захвату блокувань і очікуваний час виконання.

Урок: найшвидший спосіб витратити годину — дебажити проблему, яку ви очікували, замість тієї, про яку повідомляють wait events.

Міні-історія 2: Оптимізація, що зіграла зворотний ефект

Інша команда працювала на Percona Server з агресивно налаштованим buffer pool і гордо говорила «ми ніколи не вдаряємо диск».
Вони збільшили buffer pool «на максимально можливе» після зростання трафіку. Графіки тиждень виглядали героїчно.

Потім почався повільний інцидент: хвостова латентність зросла, але пропускна здатність не впала. CPU виглядав нормально. Диск — «зайнятий, але не шалено».
Performance Schema показав більше часу в стадіях InnoDB flushing та file I/O, і slow log почав фіксувати сплески записних затримок.

Наслідком була голова обличчя: ОС страждала від нестачі пам’яті. Buffer pool зайняв майже все, і кеш сторінок ОС та кеш метаданих файлової системи постраждали,
а фонове флашування стало більш нестабільним. Під рідкісними піковими умовами система почала свопити — достатньо, щоб зіпсувати 99-й процентиль.

Виправлення було нудно просте: трохи зменшити buffer pool, дати ОС повітря, обмежити конкуренцію з’єднань і налаштувати flush для згладжування сплесків.
«Оптимізація» покращила середні показники, але зламала хвіст. Продакшн живе у хвості.

Урок: не можна перемогти, забираючи всю пам’ять для бази. Ядро теж має голос, і воно голосує затримкою.

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

Велика команда підприємства використовувала і PostgreSQL, і Percona в різних продуктах.
Їхня найбільш цінна робота з надійності не була екзотичною настройкою; це була стандартизована інструментація до інцидентів:
логування повільних запитів з адекватними порогами, агрегація заяв (pg_stat_statements та Performance Schema digests) і послідовне збереження.

Одного дня розгортання додатку спричинило раптове уповільнення БД. Ніхто не сперечався, хто винен, бо дані вже були під рукою.
У Postgres відбиток топ-запиту подвоїв загальний час при тій же кількості викликів; EXPLAIN (ANALYZE, BUFFERS) показав додаткові читання.
У Percona pt-query-digest виявив змінений шаблон запиту і нову поведінку «Using filesort».

«Нудна практика» була pre-merge перевіркою, яка записувала плани запитів для критичних ендпоінтів і порівнювала їх після змін.
Це зробило рішення про відкат очевидним. Вони швидко відкотили, потім виправили запит і додали відсутній індекс у контрольованому розгортанні.

Інцидент був коротким не через те, що вони були геніями, а бо у них були квитанції.

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

Ось режими відмов, які постійно з’являються в продакшні, з конкретними виправленнями. Це секція, яку читають, коли кава тремтить.

1) Симптом: раптове глобальне уповільнення, багато сесій «чекають»

  • Корінна причина (Postgres): DDL блокує або черга блокувань за одним довгим транзакцією.
  • Виправлення: ідентифікуйте блокера через pg_locks/pg_stat_activity, завершіть або дочекайтеся; змініть стратегію міграцій.
  • Корінна причина (Percona): contention metadata lock (MDL), часто через DDL.
  • Виправлення: знайдіть власника блокування в processlist/Performance Schema; завершіть сесію; використовуйте практики онлайн-змін схеми.

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

  • Корінна причина (Postgres): шторм чекпойнтів (занадто часті або занадто «рвані»), іноді в поєднанні з повільним сховищем.
  • Виправлення: налаштуйте інтервали чекпойнтів і completion_target; переконайтесь, що WAL і дані на адекватному сховищі; перевірте поведінку background writer.
  • Корінна причина (Percona): шторм flush, тиск віку чекпойнта, сплески fsync.
  • Виправлення: налаштуйте InnoDB flushing і розмір redo log; згладьте модель записів; підтвердіть затримки пристрою.

3) Симптом: читальні запити сповільнюються протягом днів/тижнів

  • Корінна причина (Postgres): блоут таблиць/індексів через борг autovacuum або довгі транзакції, що заважають очищенню.
  • Виправлення: знайдіть dead tuples і історію vacuum; виправте довгі транзакції; налаштуйте autovacuum пороги по таблицях; заплануйте вікна обслуговування.
  • Корінна причина (Percona): фрагментація плюс застаріла статистика або churn buffer pool через зростання даних.
  • Виправлення: оновіть статистику; відрегулюйте buffer pool; розгляньте обережний rebuild/optimize; виправте використання запитів/індексів.

4) Симптом: «CPU низький, але запити повільні»

  • Корінна причина: очікування I/O, блокування або внутрішня конкуренція. Низький CPU — це не комплімент; це підказка.
  • Виправлення: перевірте wait events (Postgres wait_event_type; MySQL stages/waits), потім затримки сховища, потім ліміти конкуренції.

5) Симптом: відставання реплікації зростає під час піку, потім відновлюється

  • Корінна причина: репліка I/O bound при застосуванні змін; або довгі транзакції/великі батчі спричиняють затримки застосування.
  • Виправлення: налаштуйте паралелізм застосування там, де доречно, зменшіть великі транзакції, перемістіть важкі читання з репліки і підтвердіть сховище репліки.

6) Симптом: «фікс» повільного запиту допомагає недовго, потім знову погіршується

  • Корінна причина: ви лікували симптом. Поширені причини: ефекти прогрівання кешу, зміни параметрів (нестабільність плану) або дрейф статистики.
  • Виправлення: підтвердіть через відбитки запитів з часом, порівняйте плани для різних параметрів і налаштуйте routine ANALYZE/статистику з моніторингом.

Чеклісти / покроковий план для повторюваного дебагу

Чекліст A: Інструментація до інциденту (зробіть це у спокійний вівторок)

  1. Увімкніть і розмірьте агрегацію запитів. Postgres: pg_stat_statements. Percona: Performance Schema statement digest + sys views.
  2. Увімкніть лог повільних запитів з реальним порогом. Не ставте 10 секунд і називайте це спостережуваністю.
  3. Зберігайте достатньо історії, щоб охопити інцидент. Якщо ви ротуєте логи щогодини, ви втратите місце злочину.
  4. Збирайте метрики очікувань/блокувань. Postgres wait events; MySQL waits/stages; view блокувань.
  5. Стандартизуйте робочі процеси EXPLAIN. Postgres: EXPLAIN (ANALYZE, BUFFERS) для топ-запитів у стенді. MySQL: EXPLAIN плюс runtime sampling через Performance Schema.
  6. Базуйте затримки сховища. Знайте ваші нормальні r_await/w_await і поведінку fsync.

Чекліст B: Під час інциденту (15-хвилинний цикл стримування)

  1. Підтвердіть вплив для користувача. Які кінцеві точки? Читання чи запис? Один орендар чи глобально?
  2. Спочатку здоров’я хоста. CPU, iowait, пам’ять/swap, насичення диска.
  3. Ідентифікуйте блокування. Postgres lock graph; MySQL MDL/InnoDB locks.
  4. Топ відбитки запитів. Postgres statements; MySQL digests або slow log digest.
  5. Пом’якшіть безпечно. Вбийте блокера, обмежте конкуренцію, призупиніть пакетні задачі або виконайте failover, якщо репліка здорова і консистентна.
  6. Підтвердіть поліпшення. Запустіть ті самі перевірки знову. Не оголошуйте перемогу лише тому, що графік «виглядає тихіше».

Чекліст C: Після інциденту (перетворіть біль на інженерну роботу)

  1. Напишіть односторінкову хронологію. Що змінилося, коли, як виявлено, що виправили.
  2. Додайте запобіжники. Контроль міграцій, regression тест планів запитів, ліміти конкуренції, circuit breakers.
  3. Інструментуйте відсутні сигнали. Якщо вам довелося здогадуватися — додайте метрику або лог, щоб наступного разу не гадати.
  4. Зробіть фікс нудним. Найкраща реакція на інцидент — скрипт і рукопис, а не герой.

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

1) Якщо можу вибрати лише одну «обов’язкову» фічу видимості, що це?

Агрегація відбитків запитів з часом і кількістю. У Postgres це pg_stat_statements. У Percona/MySQL — statement digests (Performance Schema) і/або slow log + digest.
Без цього ви будете ганятися за окремими запитами замість форми навантаження.

2) Хто краще каже «ми чекаємо на блокування»?

PostgreSQL зазвичай ясніший і швидший: wait_event_type плюс прості join-и блокувань дають негайну історію.
Percona цілком може це зробити, але вам треба знати, де дивитися (статуси processlist, Performance Schema waits, InnoDB status) і переконатись, що це увімкнено.

3) Чи застарів slow query log, якщо в мене є Performance Schema?

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

4) Чому я бачу «Using filesort» в MySQL, коли думав, що маю правильний індекс?

Бо індекс може не відповідати напряму/стовпцям ORDER BY, або оптимізатор може обрати інший шлях доступу на основі статистики.
Виправте індекс, що відповідає сортуванню, перевірте кардинальність і переконайтесь, що ви не вибираєте стільки колонок, що це стає дорогим.

5) PostgreSQL гірший після додавання більше індексів. Хіба це не навпаки?

Індекси пришвидшують читання, але навантажують записи. Більше індексів означає більше ампліфікації записів, більше роботи vacuum і іноді гіршу поведінку кешу.
Залишайте індекси, які «платять за себе». Видаляйте ті, що не приносять користі.

6) Яка найпоширеніша причина, чому Postgres «випадково» сповільнюється?

Суміш боргу autovacuum і довгих транзакцій, які блокують очищення. Це не випадково; це відкладене обслуговування, що стало видимим.

7) Яка найпоширеніша причина, чому MySQL/Percona «випадково» сповільнюється?

Тиск flush/IO плюс конкуренція блокувань (включно з MDL), часто спричинено зміною навантаження або міграцією. І так, іноді причина — неправильно розмірений buffer pool (занадто малий або занадто великий).

8) Чи можу я дебажити уповільнення без запуску важких команд?

Так. Використовуйте легкі знімки спочатку: топ таблиці запитів, processlist/pg_stat_activity і високорівневі I/O метрики.
Обережно запускайте важкі EXPLAIN ANALYZE для контрольованих зразків і уникайте виконання їх на вже насичених системах, якщо ви не знаєте витрат.

9) Що легше експлуатувати при обмеженому досвіді з базами на виклику?

Якщо ви інвестуєте в рукописи — будь-яка система працюватиме. З коробки модель PostgreSQL «запитай SQL view — отримай правду» легша для загальних фахівців.
Percona може бути так само діагностованим, але лише якщо заздалегідь стандартизувати Performance Schema і робочі процеси slow log.

Наступні кроки: як обирати та як інструментувати

Тут має бути цитата, бо це операційна мораль історії. Ставлення Вернера Фогелса до надійності можна підсумувати так
(парафраз): Ви побудували — ви експлуатуєте — Werner Vogels.
Дебагованість — частина експлуатації, а не опціональний додаток, коли стало «гаряче».

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

  • Оберіть PostgreSQL, якщо ви хочете міцну SQL-нативну видимість, першокласні звіти про wait events і прямий шлях від сесій → очікування → блокування → заяви → плани.
    Він винагороджує дисципліну в утриманні vacuum і статистики.
  • Оберіть Percona Server, якщо ви віддані екосистемі MySQL, хочете операційні покращення і справді будете запускати Performance Schema і slow logging правильно.
    Він винагороджує стандартизований інструментарій і обережне управління конкуренцією/флашами.

Практичні наступні кроки, які можна зробити цього тижня

  1. Увімкніть відбитки запитів і перевірте збереження. Переконайтесь, що можете відповісти «топ 5 запитів за загальним часом за останню годину».
  2. Увімкніть лог повільних запитів з обгрунтованим порогом і ротацією. Тримайте достатньо історії для типового інциденту.
  3. Напишіть 15-хвилинний runbook для уповільнення. Перевірки хоста, перевірки блокувань, топ-запити, варіанти пом’якшення.
  4. Проведіть одну game day. Інжектуйте навантаження, симулюйте блокування, симулюйте насичення I/O і виміряйте, як швидко on-call доходить до достовірної гіпотези.
  5. Зробіть міграції нудними. Більшість «уповільнень бази даних» — це проблеми координації змін схеми, замасковані під інші симптоми.

Найкраща видимість — та, про яку ви не думаєте доти, поки вона не знадобиться — бо ви вже точно знаєте, де вона, і вона вже збирає докази.

← Попередня
Microsoft Zune: як «не iPod» став культовим
Наступна →
OpenVPN AUTH_FAILED: чому правильні облікові дані все одно не проходять (і що перевірити)

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