MySQL проти Percona Server: продуктивність під час піків — що змінюється на практиці

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

Базу даних не обирають у спокої. Її обирають у день, коли маркетинг «просто надішле розсилку», ваш API отримує шторм підключень, і p95 латентності перетворюється на інтерпретативний танець.

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

Що реально змінюється під час піків (не маркетинг)

Percona Server починався як «MySQL, але з ручками, які нам потрібні, і видимістю, яку хотілося б бачити в MySQL». Сучасний MySQL наздогнав за кількома пунктами, а Percona також змінився разом із апстрімом. Практичне питання сьогодні не «що швидше», а «що поводиться передбачуваніше, коли навколо палає світ». У інцидентах перемагає передбачуваність.

Головні відмінності, які проявляються під час піків

  • Контроль доступу та планування потоків: Percona Server часто поставляється з опцією thread pool, яка може приборкати шторм підключень і сплески високої конкурентності в OLTP. Стандартний MySQL Enterprise має thread pool; Community зазвичай — ні. Якщо ваш пік — це «занадто багато одночасних підключень», пул потоків може стати різницею між деградацією та повною втратою служби.
  • Поведінка InnoDB/XtraDB та додаткові змінні: історично Percona постачала XtraDB (форк/покращений InnoDB) з більшою кількістю лічильників і налаштувань. Сьогодні багато покращень пішло в апстрім, але Percona все ще частіше відкриває більше можливостей для інспекції та оперативних перемикачів, що важливо під час інциденту.
  • Початкове налаштування інструментування: Percona Server зазвичай полегшує швидкий глибокий аналіз (додаткові статусні лічильники, розширені опції повільних запитів, інколи менше тертя для включення корисних плагінів). MySQL може робити більшість із цього, але іноді потрібно заздалегідь спланувати: ввімкнення важкого інструментування під час піку — це як вирішити робити повне МРТ під час спринту.
  • Екосистема операційних інструментів: Percona Toolkit (pt-query-digest, pt-online-schema-change, pt-kill) — не «Percona Server» сам по собі, але на практиці команди приймають пакет. Під час піків це важливо: вам потрібні повторювані команди для триажу, а не артізанський SQL, набраний тремтячими руками.
  • Вибір збірок і пакування: Percona має тенденцію постачати збірки, що орієнтовані на потреби операцій (додаткові плагіни, налаштування performance schema за замовчуванням, розумні рішення телеметрії). Пакет від вендора впливає на темп оновлень і на те, що ввімкнено за замовчуванням — а саме значення за замовчуванням вирішують поведінку при несподіваному навантаженні.

Пастка: припускати, що Percona Server за визначенням «швидший» у всьому. Це не так. Частіше він більш керований і більш прозорий. Під час піків керованість і видимість перемагають теоретично швидше в лабораторії.

Короткий жарт, як обіцяно: База даних під час піку — як запрошення на зустріч: більше людей ніколи не допомагає раніше її закінчити.

Кілька корисних фактів та історія (бо значення за замовчуванням мають передісторію)

Це не факти для вікторини. Кожен пояснює, чому певна поведінка існує і чому ви побачите її в продакшені.

  1. Рання цінність Percona Server була в XtraDB: роками він постачався як покращений форк InnoDB з додатковими лічильниками й налаштуваннями, яких не вистачало в апстрімі. Ця ДНК досі проявляється у «функціях, орієнтованих на операції».
  2. Thread pool не завжди був доступний: пул потоків історично був фічею Enterprise MySQL; Percona зробила подібну функціональність доступною ширшій аудиторії через дистрибутив і плагіни.
  3. MySQL 5.6→5.7→8.0 змінив правила гри: покращення native performance schema, краща поведінка метаданих і вдосконалення реплікації зменшили потребу в форку для багатьох навантажень.
  4. Performance Schema пішов від «уникати» до «використовувати»: раніше побоювалися накладних витрат; сучасні версії можна налаштувати як дружні до продакшену, і вони є незамінними для розбору піків.
  5. Реплікація ставала багатопотоковою поетапно: від одного SQL-потоку до багатопотокового аплайєра з різними режимами планування; поведінка під час піків на репліках сильно залежить від цих налаштувань.
  6. Флешинг InnoDB еволюціонував: адаптивний флашинг і стабільніше управління контрольними точками покращилися, але ви все ще можете створити самокаральні «flush storm» поганими налаштуваннями або нереалістичними припущеннями про накопичувач.
  7. За замовчуванням змінилася автентифікація та SSL у MySQL: під час піків вартість рукшейку і руйту підключень може домінувати; версія і плагіни аутентифікації мають значення.
  8. Екосистема Percona сформувала операційні практики: pt-query-digest та інші інструменти навчали покоління SRE розглядати аналіз запитів як навичку реагування на інциденти, а не як квартальний проєкт з продуктивності.

Піки — це не «високе навантаження»: механіка відмов

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

Що пік робить із системою типу MySQL

Коли запити стрибають у 5–20× на короткий період, зазвичай ви отримуєте комбінацію:

  • Шторми підключень: нові TCP + TLS + автентифікаційні руківці і накладні витрати на потік на підключення. Навіть якщо запити дешеві, планування потоків і виділення пам’яті стають дорогими.
  • Контенція mutex/блокувань: внутрішні «гарячі точки» (локери екземплярів буферного пула, dict locks, table cache, MDL, контенція adaptive hash index або навіть контенція планувальника ОС).
  • Накопичення брудних сторінок: записи, що випереджають флашинг, призводять до зростання віку контрольної точки. Зрештою движок панікує та флашить агресивно, відбираючи I/O у читань і підвищуючи латентність.
  • Тиск на redo log: якщо redo заповнюється швидше, ніж його можна чекпоінтнути, ви отримуєте паузи. Це один із найпоширеніших сценаріїв «усе було добре, поки стало погано».
  • Відставання реплікації: сплески записів створюють бекаплог; репліки відстають, і ваше масштабування на читання перетворюється на посилення навантаження на первинний вузол, оскільки додаток повертається до нього.
  • Засмічення кешу: буферний пул забруднюється одноразовими шаблонами доступу (наприклад, сторінка промоакції сканує велику категорію), витісняючи гарячі сторінки і викликаючи тривале страждання навіть після закінчення піку.

Під час піків правильне питання рідко «як зробити запити швидшими», бо більшість піків не викликані одним повільним запитом. Вони викликані забагато розумними запитами водночас, плюс кілька патологічних, які стають смертельними тільки коли ресурси натиснуті.

Керування конкурентністю: один зайвий потік усе ще зайвий

У стандартному MySQL Community поширений режим — один потік на підключення. Під час піку ви отримуєте армію потоків, що борються за CPU, блокування і кеш-лінії. Ваша машина не просто «зайнята», вона трешиться.

Чому пул потоків Percona важливий під час піків

Пул потоків змінює форму відмови. Замість 2 000 runnable-потоків, які намагаються виконати 2 000 одночасних запитів, у вас може бути менший пул робітників, який обмежує активне виконання і ставить інші в чергу. Система деградує більш поступово: латентність зростає, але відповіді продовжують надходити.

Пули потоків не чарівні. Вони можуть приховати повільні запити (черга маскує вогонь) і можуть вводити проблеми справедливості (деякі клієнти страждають, якщо пул неправильно налаштований). Але для конкретного режиму «шторм підключень + колапс планувальника CPU» вони є практичним інструментом контролю доступу.

Що робити, якщо у вас немає пулу потоків

  • Зробіть connection pooling обов’язковим: на шарі додатка. Якщо ваш додаток відкриває нові підключення під час сплесків трафіку, ви обираєте біль.
  • Використовуйте max_connections як запобіжник: не для показухи. Встановіть значення, яке хост може витримати, і швидко відмовляйтеся вгору зі справним повтором/бекофом.
  • Захистіть первинний вузол проксі: HAProxy/ProxySQL можуть обмежувати швидкість підключень і надавати мультиплексування. Навіть грубе обмеження підключень на краю може запобігти тому, щоб база даних стала вузьким місцем.

InnoDB-флашинг і контрольні точки: куди вмирають піки

Якщо піки — ваша головна проблема, перестаньте думати про InnoDB як «движок зберігання». У піку це набір конкуруючих черг: потоки очищення сторінок, писар redo log, doublewrite, частота fsync і реальна здатність вашого сховища робити малі випадкові записи.

Класичний шаблон піка: брудні сторінки підкрадаються, потім настає flush storm

Під час сплеску записів брудні сторінки накопичуються в буферному пулі. Якщо флашинг не встигає, вікно контрольної точки зростає. Нарешті InnoDB мусить агресивно флашити, щоб просунути контрольну точку і уникнути вичерпання простору redo. Цей агресивний флаш конкурує з I/O читань і CPU, тому латентність злітає. Підсумок: найгірша латентність часто з’являється після сплеску.

Налаштування, що змінюють профіль піка

  • innodb_log_file_size / innodb_redo_log_capacity: більший розмір redo може поглинати сплески, але також збільшує час відновлення після краху і може затягувати сигнал «ви в біді».
  • innodb_flush_log_at_trx_commit: 1 — найнадійніше; 2 — компроміс між стійкістю й пропускною здатністю. Під час піків частота fsync часто є обмежувачем. Якщо ви змінюєте це, робіть це як бізнес-рішення, а не в паніці.
  • innodb_io_capacity / innodb_io_capacity_max: це підказки для фонового флашингу. Неправильні значення викликають або відкладений флашинг (паніка контрольної точки пізніше), або агресивний флашинг (постійний тиск I/O).
  • innodb_flush_neighbors: на SSD/NVMe флашинг сусідів зазвичай марна робота. На обертових дисках може допомагати. Якщо у вас сучасне сховище і це ввімкнено, ви можете платити за ностальгію.
  • innodb_lru_scan_depth і page_cleaners: впливають на те, як швидко InnoDB знаходить брудні сторінки для флашингу. Погане налаштування може створити або помпання CPU, або недостатнє очищення.

Другий жарт (і ми закінчили): Налаштовувати флашинг під час інциденту — це як міняти шини на автомагістралі: можливо, але ваш критерій успіху — «ніхто не загинув».

Реплікація під час вибухів: латентність, яку ви заробляєте

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

Де проявляються відмінності MySQL/Percona

Більшість поведінки реплікації — це апстрім MySQL, але дистрибуції Percona часто полегшують спостереження та налаштування. Великі реалії ери піків:

  • Реплікація на основі рядків зазвичай краща під час піків: реплікація на рівні запитів може поводитися дивно через недетермінованість і її складніше безпечно паралелізувати.
  • Великі транзакції — фабрики відставання: одна велика транзакція застосовується як єдине ціле. Ваша репліка сидить і робить одну річ, в той час як додаток просить свіжих читань.
  • Багатопоточні аплайєри потребують правильного режиму: «LOGICAL_CLOCK» (в термінах MySQL) і відповідне відстеження залежностей можуть допомогти. Але якщо ваше навантаження концентрує доступ на кількох рядках, паралелізм вас не врятує.
  • Налаштування стійкості на репліках: ви часто можете послабити гарантії стійкості на репліках (усвідомлено), щоб тримати їх достатньо синхронними для читань під час сплесків.

Інструментування та спостережуваність: бачити проблему, не ставши проблемою

Під час піку вам потрібні відповіді швидко: це CPU, I/O, блокування чи додаток? Різниця між «нормально» і «добре» у спостережуваності — це хвилини простою.

MySQL дає вам Performance Schema, sys schema, EXPLAIN ANALYZE і багато статусних лічильників. Percona Server часто додає або відкриває більше деталей і постачається в операційно-дружньому стані. Ключ не в бренді. Ключ у тому, чи можете ви збирати докази без створення додаткового навантаження.

Одна операційна цитата, вживана обережно: Надія — не стратегія. — James Cameron. Це краще застосовується до реагування на інциденти, ніж слід було б.

Що варто ввімкнути заздалегідь перед піками

  • Повільний лог запитів з розумними порогами (і семплінг, якщо доступний). Піки часто включають «помірно повільні запити на високій частоті».
  • Performance Schema з цільовими consumer-ами (не все одночасно). Вам потрібні waits, stages і statement digests — не обов’язково повні таблиці історії на гарячому primary.
  • Пайплайн агрегації запитів, який може працювати під навантаженням, не блокуючи базу даних (парсити логи поза хостом, не запускати важкий аналіз на primary).
  • Телеметрія на рівні ОС (iostat, vmstat, готові CPU-метрики perf), бо бази даних люблять «брехати через упущення».

Практичні завдання: команди, виводи та рішення, які ви приймаєте

Це кроки, які ви дійсно виконуєте о 02:14. Кожен містить виконувану команду, фрагмент реалістичного виводу, що це означає, і яке рішення слід ухвалити. Передумови: хост Linux, systemd, доступний клієнт MySQL, облікові дані встановлені через ~/.my.cnf або env.

Завдання 1: Перевірити, чи CPU насичений або просто є надлишок потоків

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
 6  1      0  31284  81264 923456    0    0   120   980 1800 4200 55 18 12 15  0
18  2      0  29876  79912 918332    0    0   110  1120 2600 9800 63 22  5 10  0
21  3      0  28740  79020 914120    0    0    90  1400 2900 12000 68 25  2  5  0

Значення: Високе число r (runnable threads) і низьке id вказують на тиск на CPU; підвищений cs свідчить про шторм контекстних переключень.

Рішення: Якщо cs величезне та латентність стрибкоподібна, підозрюйте занадто багато активних потоків. Розгляньте thread pool (Percona) або обмеження конкурентності через проксі/додаток. Якщо домінує wa, переключайтеся на I/O.

Завдання 2: Знайти насичення I/O та латентність

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

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          41.20    0.00   12.10   18.90    0.00   27.80

Device            r/s     w/s   rkB/s   wkB/s  rrqm/s  wrqm/s  %util  await  r_await  w_await
nvme0n1        220.0  950.0  3520.0 48200.0     0.0    10.0   99.2   18.4     4.2     21.8

Значення: Пристрій на ~99% завантаження з ~18ms await; записова латентність гірша. Це історія про флашинг/redo/doublewrite до доказів протилежного.

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

Завдання 3: Перевірити шторм підключень

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Threads%';"
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_cached    | 12    |
| Threads_connected | 1800  |
| Threads_created   | 98234 |
| Threads_running   | 280   |
+-------------------+-------+

Значення: 1800 підключених, 280 запущених. Якщо це стрибнуло швидко, ви в зоні шторма. Високе Threads_created вказує на дрібну хвильовість підключень.

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

Завдання 4: Визначити тип найтривалішого очікування (блокування vs I/O vs CPU)

cr0x@server:~$ mysql -e "SELECT EVENT_NAME, COUNT_STAR, SUM_TIMER_WAIT/1000000000000 AS seconds FROM performance_schema.events_waits_summary_global_by_event_name WHERE COUNT_STAR > 0 ORDER BY SUM_TIMER_WAIT DESC LIMIT 5;"
+--------------------------------------+------------+---------+
| EVENT_NAME                           | COUNT_STAR | seconds |
+--------------------------------------+------------+---------+
| wait/io/file/innodb/innodb_log_file  | 12899322   | 1824.51 |
| wait/synch/mutex/innodb/buf_pool     |  8933121   |  610.23 |
| wait/io/file/innodb/innodb_data_file |  4021932   |  488.11 |
| wait/lock/metadata/sql/mdl           |   832211   |  120.09 |
| wait/synch/rwlock/innodb/index_tree  |  1010021   |   98.77 |
+--------------------------------------+------------+---------+

Значення: Очікування на redo log домінують. Це fsync/redo-перенапруження; також проблеми з mutex буферного пулу.

Рішення: Розглядайте це як обмеження записів. Розгляньте компроміси стійкості тільки якщо бізнес дозволяє. Інакше зменшіть паралельність записів, виправте латентність сховища і плануйте зміну параметрів redo/flushing.

Завдання 5: Перевірити брудні сторінки InnoDB і тиск на контрольні точки

cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | sed -n '1,140p'
...
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 2197815296
Buffer pool size   131072
Free buffers       128
Database pages     129880
Old database pages 47910
Modified db pages  32120
...
LOG
---
Log sequence number          112903450192
Log flushed up to            112903120991
Last checkpoint at           112901000000
...

Значення: Високе Modified db pages означає багато брудних сторінок. Великий розрив між LSN і контрольної точкою вказує на зростання віку контрольної точки. Якщо цей розрив продовжує зростати, на вас чекає flush storm (або він вже є).

Рішення: Покращіть ефективність фонового флашингу (підкоригуйте innodb_io_capacity), зменшіть швидкість записів і переконайтесь, що сховище може витримати шаблон записів. Уникайте випадкового «set io_capacity=20000», якщо ви не знаєте можливостей пристрою.

Завдання 6: Виявити основні підписи запитів під час піку

cr0x@server:~$ mysql -e "SELECT DIGEST_TEXT, COUNT_STAR, ROUND(SUM_TIMER_WAIT/1e12,2) AS total_s FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 3\G"
*************************** 1. row ***************************
DIGEST_TEXT: SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC LIMIT ?
COUNT_STAR: 481220
total_s: 912.44
*************************** 2. row ***************************
DIGEST_TEXT: UPDATE inventory SET qty = qty - ? WHERE sku = ?
COUNT_STAR: 112881
total_s: 401.18
*************************** 3. row ***************************
DIGEST_TEXT: SELECT COUNT(*) FROM sessions WHERE expires_at > ?
COUNT_STAR: 98011
total_s: 380.02

Значення: Маєте частий SELECT і гаряче UPDATE по inventory. Під час піків «гарячі рядки» вбивають конкурентність, навіть якщо запити індексовані.

Рішення: Для гарячого UPDATE: розгляньте шардінг за ключем контенції, переробку резервування запасів або переміщення лічильника в атомний кеш з write-behind (обережно). Для SELECT: переконайтеся, що індекс підтримує ORDER BY; розгляньте кешування топ-N на користувача.

Завдання 7: Підтвердити підтримку індекса для запиту піку

cr0x@server:~$ mysql -e "EXPLAIN SELECT * FROM orders WHERE user_id = 123 ORDER BY created_at DESC LIMIT 20;"
+----+-------------+--------+------+---------------+-------------------+---------+------+------+-----------------------------+
| id | select_type | table  | type | possible_keys | key               | key_len | ref  | rows | Extra                       |
+----+-------------+--------+------+---------------+-------------------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | orders | ref  | idx_user_date | idx_user_date     | 8       | const|  60  | Using where; Using filesort |
+----+-------------+--------+------+---------------+-------------------+---------+------+------+-----------------------------+

Значення: Використовує індекс, але все одно робить filesort, що натякає, що порядок індекса не відповідає запиту (або є невідповідність сортування/COLLATION чи напрямку ASC/DESC).

Рішення: Створіть правильний складений індекс (наприклад, (user_id, created_at) з відповідним напрямком сортування на MySQL 8.0 там, де це доречно). Це зменшить CPU і роботу з тимчасовими таблицями під час піків.

Завдання 8: Перевірити тиск тимчасових таблиць (дискові тимчасові таблиці — отрута піків)

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Created_tmp%';"
+-------------------------+--------+
| Variable_name           | Value  |
+-------------------------+--------+
| Created_tmp_disk_tables | 982112 |
| Created_tmp_files       | 12011  |
| Created_tmp_tables      | 1812231|
+-------------------------+--------+

Значення: Багато дискових тимчасових таблиць. Під час піків це призводить до I/O-ампліфікації, часто на тому ж пристрої, який потрібен InnoDB для redo/data.

Рішення: Виправте запити, що викликають сорти/групування без індексів; обережно підвищте tmp_table_size/max_heap_table_size; переконайтеся, що tmpdir знаходиться на швидкому сховищі, якщо це необхідно.

Завдання 9: Виявити накопичення блокувань метаданих (DDL під час піків — хобі, не план)

cr0x@server:~$ mysql -e "SELECT OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, COUNT(*) cnt FROM performance_schema.metadata_locks GROUP BY 1,2,3,4,5 ORDER BY cnt DESC LIMIT 5;"
+-------------+---------------+-------------+-----------+-------------+-----+
| OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | LOCK_TYPE | LOCK_STATUS | cnt |
+-------------+---------------+-------------+-----------+-------------+-----+
| TABLE       | app           | users       | SHARED    | GRANTED     | 980 |
| TABLE       | app           | users       | EXCLUSIVE | PENDING     |  12 |
| TABLE       | app           | orders      | SHARED    | GRANTED     | 620 |
+-------------+---------------+-------------+-----------+-------------+-----+

Значення: EXCLUSIVE lock у стані PENDING на users, тоді як сотні тримають SHARED: хтось запустив DDL (або операцію з великим числом блокувань) і зараз блокує подальші операції, яким потрібен доступ до метаданих.

Рішення: Знайдіть блокуючу сесію і вбийте її, якщо це безпечно. Перенесіть DDL на онлайн-інструменти з обмеженнями.

Завдання 10: Знайти фактичну блокуючу сесію

cr0x@server:~$ mysql -e "SHOW PROCESSLIST;" | head -n 15
Id	User	Host	db	Command	Time	State	Info
1123	app	10.0.4.21:51244	app	Query	35	Waiting for table metadata lock	ALTER TABLE users ADD COLUMN flags INT DEFAULT 0
1188	app	10.0.4.19:50122	app	Query	2	Sending data	SELECT * FROM users WHERE id = 98322
...

Значення: ALTER чекає, але він також може бути причиною хвиль блокувань залежно від типу DDL і версії.

Рішення: Під час піку завершіть DDL, якщо це не частина контрольованої міграції та не є явно безпечним. Впровадьте політику «ніяких DDL на primary у період піка», а не надію.

Завдання 11: Перевірити відставання реплікації та стан аплайєра

cr0x@server:~$ mysql -e "SHOW REPLICA STATUS\G" | egrep -i 'Seconds_Behind|Replica_IO_Running|Replica_SQL_Running|SQL_Delay|Slave_SQL_Running_State|Last_Errno|Last_Error'
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 840
Replica_SQL_Running_State: Waiting for dependent transaction to commit
Last_Errno: 0
Last_Error:

Значення: Відставання велике, SQL-потік блокується на очікуванні залежних транзакцій — часто через великі транзакції або обмеження порядку комітів.

Рішення: Шукайте великі транзакції, зменшуйте конфлікти комітів, переконайтеся, що багатопоточні аплайєри налаштовані правильно, і розгляньте послаблення гарантій на репліках, якщо критично важливе масштабування читання.

Завдання 12: Виявити великі транзакції, що отруюють репліки

cr0x@server:~$ mysql -e "SELECT trx_id, trx_started, trx_rows_modified, trx_query FROM information_schema.innodb_trx ORDER BY trx_rows_modified DESC LIMIT 3\G"
*************************** 1. row ***************************
trx_id: 90011234
trx_started: 2025-12-30 02:11:09
trx_rows_modified: 820441
trx_query: UPDATE events SET processed=1 WHERE processed=0

Значення: Масивний UPDATE в одній транзакції. Це розірве ваш буферний пул, redo і час аплайу реплікації.

Рішення: Зупиніть її (kill session), потім переробіть задачі на чанки з обмеженими транзакціями та тротлінгом.

Завдання 13: Перевірити hit rate буферного пулу і тиск читань

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

Значення: Фізичні зчитування значні. Під час піків, якщо читання вибухають, ви можете стикнутися з нестачею пам’яті або забрудненням кешу.

Рішення: Якщо RAM дозволяє, планово збільште буферний пул. Частіше: виправте шаблон доступу (кеш, обмеження сканів), додавайте індекси і захищайте базу від одноразових «переглядів всього».

Завдання 14: Подивитися, чи table cache і ліміти відкритих файлів спричиняють дрейф

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Opened_tables'; SHOW VARIABLES LIKE 'table_open_cache';"
+---------------+--------+
| Variable_name | Value  |
+---------------+--------+
| Opened_tables | 922001 |
+---------------+--------+
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| table_open_cache | 4000  |
+------------------+-------+

Значення: Масивне Opened_tables може вказувати, що table cache занадто малий або ви маєте численні таблиці/партиції. Під час піків це додає тиск на mutex і дескриптори файлів.

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

Завдання 15: Підтвердити, чи відбувається свопінг (якщо так — зупиніть усе інше)

cr0x@server:~$ free -m
               total        used        free      shared  buff/cache   available
Mem:           32000       30910         210         130         879         420
Swap:           4096        2048        2048

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

Рішення: Негайно зменшіть споживання пам’яті: зупиніть сайдкари, лише за необхідності і обережно зменшуйте буферний пул, виправте overcommit. Довгостроково: виділіть більше RAM і тримайте хост виділеним.

Завдання 16: Перевірити, чи бінлог є вузьким місцем (поширено при піках записів)

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Binlog_cache%'; SHOW GLOBAL STATUS LIKE 'Binlog_commits';"
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| Binlog_cache_disk_use         | 1221  |
| Binlog_cache_use              | 88212 |
+-------------------------------+-------+
+----------------+--------+
| Variable_name  | Value  |
+----------------+--------+
| Binlog_commits | 922112 |
+----------------+--------+

Значення: Використання диску для binlog cache вказує на великі транзакції, що розливаються; швидкість комітів binlog показує тиск. У поєднанні з redo-waits ви можете бути обмежені комітами.

Рішення: Виправте розмір транзакцій; переконайтеся, що binlog знаходиться на швидкому сховищі; розгляньте компроміси sync_binlog тільки з чіткою позицією щодо стійкості.

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

Це послідовність «входу в інцидент». Мета — визначити клас вузького місця менш ніж за 10 хвилин, а потім застосувати найменш небезпечну міру пом’якшення.

Перше: вирішіть, чи це CPU, I/O або блокування/черга

  1. Вид з ОС: vmstat і iostat -x. Якщо iowait і await високі — ви I/O-bound. Якщо CPU idle низький з величезними контекстними переключеннями — у вас проблема з конкурентністю.
  2. Швидкі лічильники MySQL: Threads connected/running і топ очікувань з Performance Schema. Шукайте «redo log waits», «data file waits», «MDL» або mutex-гарячі точки.
  3. Processlist: Чи запити застрягли в «Waiting for table metadata lock», «Waiting for commit», «Sending data», чи просто «Sleep» з надто великою кількістю підключень?

Друге: класифікувати тип піка

  • Шторм підключень: Threads_connected різко зростає, CPU трешиться, багато коротких запитів. Виправлення: pooling, thread pool/проксі, обмеження конкурентності.
  • Сплеск записів: redo waits, вік контрольної точки, багато брудних сторінок, iostat показує write await. Виправлення: загальмувати писачів, зменшити розмір транзакцій, налаштувати флашинг і redo capacity (планово).
  • Потік читань / пропуск кешу: Innodb_buffer_pool_reads стрибає, iostat показує читання, CPU може бути помірним. Виправлення: кеш, індекси, захист від сканів, додати репліки для читань (уважно до відставання).
  • Контенція блокувань / гарячі рядки: багато сесій чекають на блокування рядків, високі lock waits. Виправлення: переробити гарячу точку, знизити ізоляцію там, де безпечно, зменшити обсяг транзакцій, запровадити повторні спроби з ідемпотентністю.
  • Подія DDL/MDL: MDL waits вибухають. Виправлення: вбити/зупинити DDL, використовувати інструменти online-migration з тротлінгом.

Третє: оберіть пом’якшення, яке не ускладнить відновлення

  1. Знизьте навантаження: обмежте швидкість, тимчасово вимкніть дорогі кінці, зупиніть пакетні завдання, обмежте конкурентність.
  2. Стабілізуйте конкурентність: зменште max_connections (так, справді), вимагайте pooling, ввімкніть thread pool де доступно.
  3. Зупиніть кровотечу: вбийте найбільшого порушника (велику UPDATE, запит-з-бігуном чи випадкове повне сканування), а потім продовжуйте аналіз.

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

1) Симптом: латентність стрибає кожні кілька хвилин, навіть після падіння трафіку

Корінна причина: тиск на контрольні точки і flush storm; фоновий флашинг не встигає під час сплесків, тож потім «наздоганяє» болісно.

Виправлення: скорегуйте innodb_io_capacity під реальні IOPS пристрою, перевірте redo capacity, зменшіть розмір транзакцій і переконайтеся в стабільності латентності сховища. Не «вирішуйте» це шляхом вимкнення стійкості, якщо це явно неприйнятно бізнесом.

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

Корінна причина: надто багато runnable-потоків, контекстні переключення, контенція mutex. Часто викликано штурмом підключень.

Виправлення: примусьте pooling підключень; обмежте конкурентність; використовуйте thread pool (Percona Server) або ProxySQL для мультиплексування. Перевірте додаток на наявність відкриття підключень під кожен запит.

3) Симптом: репліки відстають, потім первинний валиться

Корінна причина: трафік для читання «повертається» на primary, коли репліки відстають; або додаток вимагає read-your-writes і обходить репліки під час відставання.

Виправлення: визначте вимоги щодо консистентності; впровадьте маршрутизацію з урахуванням відставання; налаштуйте паралелізм аплайєра; розбийте великі транзакції.

4) Симптом: раптове «Waiting for table metadata lock» скрізь

Корінна причина: неправильне онлайн-зміна схеми або проста DDL, виконана на гарячій таблиці під час піку.

Виправлення: вбийте DDL; використовуйте інструменти онлайн-зміни схеми з тротлінгом; плануйте міграції; зафіксуйте заборону DDL під час піків як політику.

5) Симптом: багато дискових тимчасових таблиць, високий I/O і запити, які «повинні бути індексовані»

Корінна причина: відсутні складені індекси для ORDER BY/GROUP BY або запити повертають занадто багато рядків; тимчасові таблиці переповнюються на диск під час конкурентності.

Виправлення: додайте правильні індекси; змініть запити, щоб вони обмежували обсяг даних раніше; розгляньте покриття індексами; уникайте SELECT *, коли потрібно лише кілька колонок.

6) Симптом: «Lock wait timeout exceeded» під час піків на одній таблиці

Корінна причина: гарячий рядок або гарячий лист вторинного індексу, часто через лічильники, прапорці стану або оновлення «last_seen».

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

7) Симптом: ввімкнення інструментування робить пік гіршим

Корінна причина: увімкнення занадто багатьох consumer-ів Performance Schema або важкого логування під пік.

Виправлення: попередньо налаштуйте легкі digests і зведення waits. Під час інцидентів віддавайте перевагу семплінгу і зведенням. Збирайте логи поза хостом.

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

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

Компанія мала «простий» план: перейти з старішого MySQL на новіший білд, залишити схему і все. Навантаження було здебільшого читабельним. Вони припустили, що піки теж будуть читабельні. Вони помилилися найбанальнішим способом.

Запуск продукту створив сплеск записів від подій трекінгу. Кожен запит записував рядок і оновлював агрегат на користувача. Оновлення агрегатів було «крихітним», тож ніхто не хвилювався. У тихий день це було нормально. В день запуску таблиця агрегатів стала найгарячішим об’єктом.

Коли латентність почала зростати, команда масштабувала додаток горизонтально. Це підвищило конкурентність, що збільшило контенцію блокувань на гарячих рядках. Відставання реплікації зросло, репліки відстали, і більше читань пішло на primary. Primary не «закінчився з CPU». Він втратив терпіння.

Хибне припущення було в тому, що «маленькі записи дешеві». Маленькі записи дешеві до того моменту, коли вони серіалізуються. Виправлення не полягало в новому розмірі інстансу. Потрібно було змінити дизайн: перенести агрегати в асинхронний конвеєр, використовувати події append-only і перераховувати агрегати поза лінією. Вони також додали обмеження конкурентності на проксі, щоб база могла деградуватися, не руйнуючись.

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

Команда побачила redo waits під час піку і вирішила зробити коміти швидшими, послабивши стійкість на primary. Вони змінили налаштування flush-at-commit під час квартального навантажувального тесту і отримали гарний графік пропускної здатності. SRE, який це дозволив, досі шкодує.

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

Дні після цього вони витратили на звірку платежів і дій користувачів з логів додатка. Ніхто не отримував задоволення. Проблема була не лише в тому, що вони втратили дані; вони втратили довіру. Клієнти помічають це раніше, ніж 200ms стрибок латентності.

Урок був жорсткий: зміни стійкості — це продуктові рішення. Якщо ви хочете їх послабити, потрібні компенсуючі контролі: ключі ідемпотентності, звірки і чітке прийняття RPO/RTO. Інакше ви «оптимізуєтеся» до зустрічі з аудитом.

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

Інша компанія працювала з Percona Server на первинних вузлах і звичайним MySQL на внутрішніх сервісах. Їхня відмінність не в бінарнику. Вона в дисципліні: у них був плейбук, вони практикували його і попередньо ввімкнули потрібне інструментування з низькими накладними витратами.

Коли інтеграція партнера вийшла з-під контролю і почала бомбардувати кінцеву точку, база показала підвищені redo waits і зростання віку контрольної точки. За кілька хвилин on-call витягнув топ waits, ідентифікував злісний запит по digest і зіставив його з feature flag. Вони вимкнули його. Потім загальмували партнера на краю мережі.

Оскільки в них була правила: жодних великих транзакцій і всі пакетні задачі розбиті на чанки, система швидко відновилася. Репліки відстали, але не стали непридатними. Без героїчних налаштувань. Без несподіваних змін конфігурації. Просто нудні запобіжні заходи, що працюють за задумом.

Постмортем виглядав як список покупок: посилити ліміти швидкості, додати кеш для тієї кінцевої точки і додати алерт для зростання віку контрольної точки. Ніхто не отримав медалі. Всі спали спокійно.

Чеклісти / покроковий план

Загартування перед піком (зробіть це завчасно)

  1. Забезпечте connection pooling у кожному сервісі. Аудитуйте з canary, що логує частоту створення підключень.
  2. Встановіть розумний max_connections і протестуйте поведінку при досягненні. Правильна поведінка — «контрольована відмова», не «DB OOM».
  3. Увімкніть легке спостережуваність: slow query log (з розумним порогом), Performance Schema digests + зведення waits і відправлення метрик ОС.
  4. Визначте позицію щодо стійкості: innodb_flush_log_at_trx_commit і sync_binlog — це не налаштування продуктивності; це політика щодо втрати даних.
  5. Перевірте латентність сховища під час сплесків записів. Виміряйте fsync і продуктивність малих випадкових записів; не довіряйте пиковим числам вендора.
  6. Розбивайте всі пакетні завдання за замовчуванням: обмежені транзакції, паузи між чанками і механізм «зупиняти при відставанні репліки».
  7. Практикуйте відновлення і маршрутизацію читань з урахуванням відставання. Якщо репліки відстають, маршрутизація має деградувати плавно.

Під час піку (безпечна послідовність)

  1. Стабілізуйте пацієнта: лімітуйте швидкість, вимкніть непотрібні функції, зупиніть пакетні завдання і обмежте конкурентність.
  2. Класифікуйте вузьке місце: CPU vs I/O vs блокування (Швидка діагностика вище).
  3. Вбийте найбільшого порушника: одну велику транзакцію, runaway-звітний запит або випадковий DDL.
  4. Захистіть реплікацію: тримайте репліки «достатньо хорошими» для читань; зменшуйте патерни великих транзакцій.
  5. Збирайте докази: збережіть топ waits, топ digest-ів, знімки iostat і зразки processlist для постмортему.

Після піку (не пропустіть цю частину)

  1. Перетворіть пом’якшення на політику: feature flag, ліміти швидкості, розбиття на чанки, шлюз DDL.
  2. Виправте форму запитів: індекси, перепис запитів, зменшення тимчасових таблиць, робота з гарячими рядками.
  3. Повторіть тренування піків: та сама ситуація має бути нудною наступного разу.

Поширені запитання

1) Чи Percona Server — це «просто MySQL з додатками»?

В основному так — та сама основа, плюс специфічні фічі Percona, вибір пакування і часто більш дружнє до операцій інструментування. Під час піків «додатки» важливі, коли вони дають контроль доступу (thread pool) і кращу видимість.

2) Якщо я на MySQL 8.0, чи все ще виграю від Percona Server?

Можливо. Покращення в MySQL 8.0 зменшують розрив. Рішення зводиться до: чи потрібно вам наявність пулу потоків в Percona, певні плагіни і операційні значення за замовчуванням — і чи довіряєте ви своєму пайплайну оновлень із цим дистрибутивом?

3) Який патерн піка найчастіше виграє з Percona Server?

Шторми підключень і високо-конкурентні OLTP, де поведінка пулу потоків запобігає колапсу планувальника CPU. Якщо ваші піки переважно I/O-bound через записи, перевага більше в інструментуванні та ергономіці налаштування, ніж у чистій пропускній здатності.

4) Чи пул потоків зменшить мою латентність?

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

5) Чи слід змінювати налаштування стійкості, щоб пережити піки?

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

6) Чому найгірша латентність трапляється після завершення піку?

Тому що InnoDB має виплатити борг: флашити брудні сторінки і просунути контрольні точки. Пік створює борг; flush storm — це збирач боргу.

7) Як відрізнити «нормальний» backlog реплікації від «застряглого»?

Якщо відставання поступово зменшується після зниження швидкості записів — це backlog. Якщо воно вирівнюється — перевіряйте стан аплайєра на commit dependency waits, lock waits або помилки; шукайте величезні транзакції і події MDL.

8) Чи кращі індекси можуть вирішити проблеми піків?

Іноді так — особливо для хвиль читання і тимчасових таблиць на диску. Але індекси не виправлять шторми підключень, fsync-ліміти redo log чи конфлікти на гарячих рядках. Спершу діагностуйте клас вузького місця.

9) Який найсильніший «швидкий виграш» для зниження ризику піків?

Connection pooling плюс обмеження конкурентності (через проксі або додаток). Це нудно, вимірювано і не грає з коректністю.

10) Чи слід увімкнути важке інструментування тільки на репліках?

Часто це хороша компромісна міра. Тримайте primary з легким інструментуванням; запускайте глибший трасинг запитів або важчі consumer-и Performance Schema на репліці, яка достатньо дзеркалить навантаження, щоб бути корисною.

Наступні кроки, які ви справді можете зробити цього тижня

  1. Проведіть drill піків у staging: симулюйте 10× ріст підключень і 5× сплеск записів. Захопіть vmstat/iostat і топ waits Performance Schema.
  2. Вирішіть щодо контролю доступу: якщо ви можете використати пул потоків Percona (або проксі), впровадьте його; інакше забезпечте pooling і жорстко обмежте підключення.
  3. Аудитуйте великі транзакції: знайдіть і розбийте пакетні задачі; встановіть запобіжники, щоб один «UPDATE everything» не пройшов непоміченим.
  4. Виміряйте поведінку контрольних точок під час навантажувальних тестів: перевірте зростання брудних сторінок і патерни redo-waits; налаштуйте підказки флашингу під реальне сховище.
  5. Напишіть ваш on-call плейбук, використовуючи послідовність Швидкої діагностики і команди вище. Попрактикуйте його один раз, коли ніхто не панікує.

Якщо ви обираєте між MySQL і Percona Server саме через продуктивність під час піків, оберіть той, що дає вам кращий контроль доступу і швидший шлях до істини під час інцидентів у вашому середовищі. Бенчмарки вас не будуть дзвонити. Піки — так.

← Попередня
До NVIDIA: що означало «графіка», коли 3D був розкішшю
Наступна →
Proxmox vs ESXi для ZFS: вибір шляху контролера диска (HBA passthrough чи віртуальні диски)

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