MariaDB vs MySQL: єдиний чекліст, що знаходить вузькі місця швидше за підлаштування параметрів

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

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

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

Один чекліст: знайдіть вузьке місце, потім налаштовуйте

MariaDB і MySQL мають багато спільного, особливо якщо ви переважно працюєте в InnoDB‑світі. Саме тому питання «який швидший» зазвичай невірне. Правильне питання: що зараз обмежує пропускну здатність або затримку на цьому хості? CPU. I/O. Блокування. Пам’ять. Мережа. Реплікація. Або класика: один патологічний запит, що тягне все інше в багнюку.

Ось ментальна модель, яка тримає вас у рамках:

  • Вузьке місце пропускної здатності: ви вичерпуєте якийсь ресурс (CPU, пропускну здатність диска, мютекси). Симптоми: QPS стагнує при зростанні навантаження; затримка може, але не обов’язково, підскочити.
  • Вузьке місце затримки: щось час від часу блокує (fsync‑пики, блокування, пропуски в буфер‑пулі, затримка в застосуванні реплікації). Симптоми: хвіст затримки зростає; «іноді повільно».
  • Чергове вузьке місце: однонитковий або послідовний етап (скидання redo‑логу, SQL‑потік репліки, metadata lock). Симптоми: одна черга росте; усі чекають своєї черги.

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

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

І так, ви все одно будете налаштовувати. Але ви робитимете це з доказами.

Короткий жарт №1: «Ми налаштували MySQL поки він не став швидким.» Мило. Балансувальник навантаження це пам’ятає.

Факти та історія, що й досі важливі в продакшні

Трохи контексту допомагає, бо пояснює, чому відрізняються значення за замовчуванням, чому поведінка дивує і чому чийсь «порадник для MySQL» ламає MariaDB (або навпаки).

  1. MariaDB була форкнута з MySQL у 2009 році після занепокоєння через придбання MySQL компанією Oracle. Це не просто дрібниця: саме тому припущення екосистеми з часом розходяться.
  2. Ранні версії MariaDB активно позиціонувалися як «прямий замінник». Операційно це здебільшого правда на рівні SQL — але поведінка продуктивності може відрізнятися на краях (оптимізатор, thread pool, можливості реплікації).
  3. MySQL 5.6/5.7 зробили InnoDB центром тяжіння і значно покращили performance schema та інструментарій. Якщо ви все ще ставитеся до нього як до чорної скриньки — ви втрачаєте безкоштовну діагностику.
  4. MySQL 8.0 видалив query cache (що відоме було проблемами з mutex‑контенцією). Якщо у вашій MariaDB‑флоті все ще працює query cache — це історичний артефакт із сучасними наслідками.
  5. MariaDB відрізнилася функціями на кшталт Aria, ColumnStore і інтеграції з Galera (в багатьох дистрибутивах). Це не просто фічі; вони змінюють режими відмов і операційні ручки.
  6. Перебудова словника даних у MySQL 8.0 змінила метадані й деякі реалії оновлень/відкатів. «Працювало на 5.7» — це не план.
  7. Обидві системи сильно залежать від поведінки Linux I/O (fsync, скидання брудних сторінок, планувальник). Багато «інцидентів бази даних» — це просто ядро, яке робить те, що ви попросили, а не те, що ви мали на увазі.
  8. Реплікація еволюціонувала по‑різному: MySQL зосередився на GTID, багатонитковій реплікації, груповій реплікації; MariaDB має власну реалізацію GTID і часто іншу ергономіку реплікації. Ті ж слова — інші підводні міни.

Також: розмови про продуктивність швидко старіють. Керівництво з тюнінгу, написане для MySQL 5.5 на шпиндельних дисках, може завдати шкоди на MySQL 8.0 на NVMe з cgroups і шумними сусідами.

Потрібне цитатне правило, перефразована ідея: перефразована ідея: «Надія — не стратегія.» — часто в операційних кругах; використовуйте як принцип, готовий до виклику, а не як слоган.

Плейбук швидкої діагностики (перший/другий/третій)

Це порядок дій «увійти в палаючу кімнату». Він розроблений для швидкості, а не для вишуканості. Мета — визначити, чи ви CPU‑bound, IO‑bound, lock‑bound або replication‑bound за декілька хвилин.

Перший: доведіть, який тип вузького місця у вас

  • Чи насичений ресурс на хості? CPU, тиск пам’яті, IO wait, латентність диска, помилки мережі.
  • Чи чекає база даних? очікування блокувань, fsync/redo очікування, пропуски в буфер‑пулі, планування потоків, сплески підключень.
  • Чи виконує база щось дороге? повні скани, погані плани, сортування на диску, тимчасові таблиці, відсутні індекси.

Другий: знайдіть головного порушника, а не середнє

  • Топ‑запити за сумарним часом (не за кількістю).
  • Топ‑очікування (блокування, I/O, flush, metadata locks).
  • Топ‑таблиці (гарячі рядки, відсутні індекси, сильний churn).

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

  • Пом’якшити зараз: вбити неконтрольовані запити, знизити навантаження, обережно збільшити розміри пулів, зменшити конкурентність, перенести читання на репліки.
  • Виправити далі: зміни індексів, перепис запитів, зміни схеми, партиціонування, корекція рівнів ізоляції.
  • Масштабувати за потреби: більше CPU для CPU‑bound, швидше сховище для I/O‑bound, репліки для read fan‑out, шардинг лише якщо ви любите страждання (або дійсно це потрібно).

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

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

Це виконувані завдання, які можна зробити на Linux DB‑хості та всередині бази даних. Кожне містить (a) команду, (b) що означає вивід, і (c) рішення, яке ви приймаєте. Остання частина — різниця між моніторингом і операціями.

Завдання 1: підтвердити, який движок і версія у вас реально запущені

cr0x@server:~$ mysql -e "SELECT VERSION() AS version, @@version_comment AS comment, @@version_compile_machine AS arch;"
+------------------------+------------------------------+------+
| version                | comment                      | arch |
+------------------------+------------------------------+------+
| 10.6.16-MariaDB-1:...  | MariaDB Server               | x86_64 |
+------------------------+------------------------------+------+

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

Рішення: Оберіть правильні інструменти: робочі процеси, які інтенсивно покладаються на performance_schema, більш зрілі в MySQL 8.0; у MariaDB можуть знадобитися інші лічильники статусу та еквіваленти sys schema.

Завдання 2: швидка перевірка навантаження хоста (CPU, IO wait, черга виконання)

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      0  81232  64312 987654    0    0   120   980  900 2100 25 10 45 20  0
 8  2      0  79012  64320 987120    0    0   110  1600 1100 3200 35 12 33 20  0

Значення: Високе r вказує на конкуренцію за CPU; високе wa — на очікування I/O; стійке b означає заблоковані задачі.

Рішення: Якщо wa стабільно високий і латентність стрибкоподібна, переходьте прямо до завдань по дисковій латентності. Якщо r високий при низькому wa, зосередьтеся на CPU‑профілюванні запитів і контенціях.

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

cr0x@server:~$ iostat -x 1 3
Device            r/s   w/s  rkB/s  wkB/s  await  svctm  %util
nvme0n1          1200  9000  48000 320000   18.5   0.3   92.0

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

Рішення: Якщо await підвищується одночасно з латентністю БД — ви I/O‑bound. Пом’якшення: зменшити пикові записи (чекпоінт/flush), покращити buffer pool або перейти на швидше сховище. Якщо await низький, а БД повільна — вузьке місце десь інде.

Завдання 4: знайти найзавантаженіші потоки MySQL/MariaDB на рівні ОС

cr0x@server:~$ top -H -p $(pidof mysqld)
top - 12:01:22 up 34 days,  2 users,  load average: 9.10, 8.80, 7.90
Threads:  210 total,   8 running, 202 sleeping
%Cpu(s): 70.0 us, 10.0 sy,  0.0 ni, 20.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
12345 mysql     20   0 12.3g  8.1g  0.0g R  180.0 52.0  12:11.04 mysqld

Значення: Якщо ви бачите кілька потоків, що зашкалюють, можливо, у вас CPU‑bound запити або мютекс‑контенція. Якщо CPU низький, але затримка висока — зосередьтеся на очікуваннях/блокуваннях/IO всередині InnoDB.

Рішення: Якщо CPU насичений — переходьте до профілювання запитів і планів. Якщо ні — перевіряйте блокування й IO‑очікування в InnoDB.

Завдання 5: миттєво виявити неконтрольовані запити

cr0x@server:~$ mysql -e "SHOW FULL PROCESSLIST\G" | sed -n '1,80p'
*************************** 1. row ***************************
     Id: 123
   User: app
   Host: 10.0.2.14:51234
     db: prod
Command: Query
   Time: 88
  State: Sending data
   Info: SELECT ... FROM orders JOIN order_items ...

Значення: Time — скільки часу виконується. State підказує, що відбувається (не завжди точно). Велика кількість довготривалих станів «Sending data» часто означає великі скани/з’єднання.

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

Завдання 6: швидко знайти контенцію блокувань (InnoDB)

cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | sed -n '1,140p'
TRANSACTIONS
------------
Trx id counter 987654321
Purge done for trx's n:o < 987654000 undo n:o < 0 state: running
History list length 12045
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 987654310, ACTIVE 92 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 123, OS thread handle 1400, query id 7777 10.0.2.14 app updating
UPDATE accounts SET balance=balance-? WHERE id=?

Значення: Довгі «ACTIVE» транзакції та великий History list length часто корелюють з відставанням purge, зростанням undo та вторинними симптомами типу «рандомні повільні запити».

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

Завдання 7: перевірити навантаження на buffer pool та індикатори hit ratio

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads'; SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';"
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| Innodb_buffer_pool_reads| 124009876 |
+-------------------------+-----------+
+----------------------------------+-------------+
| Variable_name                    | Value       |
+----------------------------------+-------------+
| Innodb_buffer_pool_read_requests | 98012345678 |
+----------------------------------+-------------+

Значення: Innodb_buffer_pool_reads — це фізичні читання. Якщо вони швидко ростуть відносно read requests — ви промахуєте кеш і платите дисковою латентністю.

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

Завдання 8: виявити витік у тимчасові таблиці (сортування/з’єднання на диску)

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Created_tmp%tables';"
+-------------------------+----------+
| Variable_name           | Value    |
+-------------------------+----------+
| Created_tmp_disk_tables | 9123456  |
| Created_tmp_tables      | 12345678 |
+-------------------------+----------+

Значення: Високе співвідношення тимчасових таблиць на диску свідчить про занадто маленькі пам’ятні ліміти для сортувань/з’єднань або про запити, що по суті спадають на диск (відсутні індекси, великі GROUP BY).

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

Завдання 9: перевірити тиск на redo log та проблеми з фсинком

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_os_log_fsyncs'; SHOW GLOBAL STATUS LIKE 'Innodb_log_waits';"
+----------------------+----------+
| Variable_name        | Value    |
+----------------------+----------+
| Innodb_os_log_fsyncs | 77881234 |
+----------------------+----------+
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| Innodb_log_waits | 12345 |
+------------------+-------+

Значення: Innodb_log_waits вказує на очікування місця в буфері логу/флашу. У поєднанні з високою частотою fsync і дисковою латентністю це вказує на вузьке місце записів.

Рішення: Якщо log waits зростають — ви досягаєте межі commit/flush. Пом’якшення: швидше сховище, зменшення частоти транзакцій, батчинг записів або зміна налаштувань надійності лише з явним погодженням бізнесу.

Завдання 10: перевірити відставання репліки і чи це I/O, SQL apply або блокування

cr0x@server:~$ mysql -e "SHOW SLAVE STATUS\G" | egrep -i "Seconds_Behind_Master|Slave_IO_Running|Slave_SQL_Running|Relay_Log_Space"
Seconds_Behind_Master: 420
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Relay_Log_Space: 987654321

Значення: IO‑потік в порядку, SQL‑потік працює, але відставання значне й relay log накопичується. Репліка може завантажувати, але не встигає застосувати транзакції.

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

Завдання 11: перевірити сплески підключень і планування потоків

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Threads%'; SHOW GLOBAL STATUS LIKE 'Connections';"
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_connected | 980   |
| Threads_running   | 64    |
| Threads_cached    | 0     |
+-------------------+-------+
+---------------+--------+
| Variable_name | Value  |
+---------------+--------+
| Connections   | 987654 |
+---------------+--------+

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

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

Завдання 12: порівняти розмір таблиць/індексів з пам’яттю, щоб передбачити промахи кеша

cr0x@server:~$ mysql -e "SELECT table_schema, SUM(data_length+index_length)/1024/1024/1024 AS gb FROM information_schema.tables GROUP BY table_schema ORDER BY gb DESC;"
+--------------+--------+
| table_schema | gb     |
+--------------+--------+
| prod         | 820.12 |
| mysql        | 0.05   |
+--------------+--------+

Значення: Якщо ваш активний датасет — сотні ГБ, а buffer pool — 32GB, ви не кешуєте робочий набір. Читання будуть йти з диска. Плани мають бути селективними.

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

Завдання 13: зловити накопичення metadata locks (DDL та «безпечні» ALTER)

cr0x@server:~$ mysql -e "SHOW FULL PROCESSLIST;" | awk '$6 ~ /Waiting/ || $7 ~ /metadata/ {print}'
234 app 10.0.2.21:49910 prod Query 35 Waiting for table metadata lock ALTER TABLE users ADD COLUMN ...

Значення: Metadata locks блокують запити, що звертаються до таблиці. Один DDL може заморозити гарячий шлях таблиці, якщо він чекає за довгою транзакцією.

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

Завдання 14: швидко переглянути slow query log (якщо увімкнено)

cr0x@server:~$ sudo tail -n 20 /var/log/mysql/slow.log
# Time: 2025-12-31T11:55:22.123456Z
# Query_time: 12.400  Lock_time: 0.000 Rows_sent: 50  Rows_examined: 50000000
SELECT ... FROM events WHERE customer_id=... ORDER BY created_at DESC LIMIT 50;

Значення: Кількість переглянутих рядків (Rows examined) — показник. 50M переглянутих, щоб повернути 50 рядків — це проблема індексу або плану, а не «більше buffer pool».

Рішення: Додайте/виправте індекс (ймовірно (customer_id, created_at)), перевірте через EXPLAIN і подумайте над переписуванням запиту. Не «налаштовуйте» навколо відсутнього індексу.

Завдання 15: перевірити план через EXPLAIN і зробити його читабельним

cr0x@server:~$ mysql -e "EXPLAIN SELECT id FROM events WHERE customer_id=42 ORDER BY created_at DESC LIMIT 50\G"
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: events
         type: ALL
possible_keys: idx_customer_created
          key: NULL
      rows: 50000000
     Extra: Using where; Using filesort

Значення: type: ALL і filesort на великій таблиці вказують на повний скан + сортування. Індекс існує, але не використовується (неправильний порядок, низька селективність, невідповідна колація, функція над колонкою або старі статистики).

Рішення: Виправте запит або індекс. Якщо статистики застаріли — виконайте analyze. Якщо оптимізатор помиляється, розгляньте hint‑и лише як останній засіб і тільки після тестів на регресії.

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

cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | sed -n '/LATEST DETECTED DEADLOCK/,+60p'
LATEST DETECTED DEADLOCK
------------------------
*** (1) TRANSACTION:
TRANSACTION 987654400, ACTIVE 2 sec updating or deleting
...
WE ROLL BACK TRANSACTION (1)

Значення: Дедлоки — це баги конкуренції, а не «баг бази даних». Зазвичай спричинені непослідовним порядком оновлень або надто великими транзакціями.

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

Завдання 17: виміряти файлову систему і опції монтування, що впливають на fsync

cr0x@server:~$ mount | egrep ' /var/lib/mysql | /data '
/dev/nvme0n1p2 on /var/lib/mysql type ext4 (rw,noatime,data=ordered)

Значення: Файлова система та опції монтування впливають на оновлення метаданих і write barriers. Вам не потрібні екзотичні налаштування; потрібні послідовні, протестовані налаштування.

Рішення: Якщо ви бачите fsync‑зависання — перевірте прошивку диска, стан файлової системи і журнали ядра перед тим, як чіпати налаштування надійності бази.

Завдання 18: переконатися, що база не свопиться (це тихо все зруйнує)

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:            31Gi        28Gi       0.8Gi       0.2Gi        2.2Gi       1.1Gi
Swap:            4Gi        1.9Gi       2.1Gi

Значення: Якщо на DB‑хості використовується swap, латентність стає непередбачуваною. Навіть «трохи swap» може означати, що сторінки, що вам важливі, викидаються.

Рішення: Зменшіть пам’ятний слід (buffer pool занадто великий, пер‑з’єднання буфери занадто великі), додайте RAM або обмежте конкуренцію. Не звикайте до swap на базах даних, якщо вам подобаються детективні історії.

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

Це операційний чекліст, який я хочу бачити на стіні (або в runbook). Це мінімальний набір кроків, що надійно приводить до відповіді без тюнінгу за фольклором.

Чекліст A: 10‑хвилинна тріаж (продакшн у вогні)

  1. Підтвердити область впливу: один ендпойнт, один орендар чи все? Зхопіть часове вікно і показники помилок з моніторингу додатка.
  2. Насичення хоста: запустіть vmstat і iostat -x. Вирішіть: CPU‑bound чи IO‑bound.
  3. Черга в базі: SHOW FULL PROCESSLIST, щоб побачити, чи багато станів «Waiting for …» або кілька довгих виконань.
  4. Блокування: SHOW ENGINE INNODB STATUS і пошук довгих транзакцій, очікувань блокувань, дедлоків.
  5. Реплікація (якщо доречно): перевірте відставання. Якщо читання віддаються з реплік, відставання — це помилка, яку бачить користувач.
  6. Термінове пом’якшення: вбийте найгірший запит, знизьте навантаження, призупиніть DDL або тимчасово направте трафік на читання інакше.
  7. Захопіть докази: уривок зі slow log, EXPLAIN, відповідні лічильники статусу перед перезапуском будь-чого.

Чекліст B: 1‑годинна діагностика (знайдіть справжнє вузьке місце)

  1. Топ‑запити за сумарним часом: використовуйте агрегацію slow log або перегляди performance schema (якщо доступні). Зосередьтеся на сумарному часі, а не лише на найдовшому окремому запиті.
  2. Валідація плану: EXPLAIN для найгірших; шукайте повні скани, filesort, тимчасові таблиці, погане порядкування джоінів.
  3. Класифікація очікувань: визначте, чи очікування здебільшого I/O, блокувань чи CPU. У MySQL 8.0 performance_schema може це чітко показати; у MariaDB більше покладайтеся на InnoDB status + лічильники статусу.
  4. Перевірка форми даних: розміри таблиць, розміри індексів, нерівномірний розподіл, гарячі рядки.
  5. Перевірка конкуренції: активні потоки, підключення, обсяг транзакцій, розміри батчів.
  6. Перевірка сховища: журнали ядра, латентність диска, поведінка файлової системи, сплески writeback.
  7. Визначте клас виправлення: запит/індекс, поведінка додатка, сховище або ємність.

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

  1. Спочатку виправлення запитів/індексів (найвищий ROI, найнижчий системний ризик). Робіть зміни тестованими й відкатними.
  2. Друге — контролі конкуренції (пулінг, черги, обмеження). Запобігайте thundering‑herds.
  3. Третє — розмір пам’яті (buffer pool, буфери на з’єднання). Слідкуйте за swap і OOM.
  4. Останні — I/O/налаштування надійності і лише з явним прийняттям ризику. Ви можете купити продуктивність за рахунок надійності; відновити надійність після втрати даних не вдасться.

Короткий жарт №2: Вимкнення надійності — це як зняти детектор диму, бо він шумить. Ви спатимете спокійно аж до моменту, коли вже ні.

Три міні‑історії з корпоративного світу (реалістично, анонімізовано)

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

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

Хибне припущення було тонким: вони думали, що їхнє навантаження залежить від запитів. Насправді — ні. У продакшні була висока конкурентність операцій запису зі періодичними сплесками — щогодинні підсумки рахунків, кожна транзакція торкалася кількох гарячих рядків. Додаток використовував довгі транзакції з додатковими читаннями «щоб бути впевненим». Ці читання не були безпечні. Вони утримували блокування й посилювали тиск purge.

За день почали з’являтися «випадкові» повільні запити. Потім підкралося відставання реплікації. Потім одна репліка була підвищена під час іншого вікна обслуговування і система розвалилася: новий primary мав великий history list length, сховище мало періодичні fsync‑зависання, і додаток підсилював це відкриттям нових підключень під час повторів.

Спочатку вони звинуватили вибір движка. Це було зручно і невірно. Справжній винуватець — обсяг транзакцій і контенція гарячих рядків. Коли вони скоротили транзакції, додали відсутній композитний індекс і ввели обмежені повтори, обидва движки — MariaDB і MySQL — працювали добре. Движок важив менше, ніж дисципліна робочого навантаження.

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

Міні‑історія 2: оптимізація, що відбилася бумерангом

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

Зміни ввели в п’ятницю (не зі зла, а через оптимізм). Під час піку кількість підключень зросла через проблему upstream pooler. Тепер кожне підключення могло алокувати більше пам’яті для сортувань і з’єднань. Тиск пам’яті підскочив, ядро почало свопити, і раптом кожний запит — швидкий чи повільний — став повільнішим. Латентність стала стрибкоподібною, потім вирівнялася на високому рівні.

Вони швидко відкотили, але урок запам’ятався: пам’ятні налаштування множаться на конкурентність. «Більші буфери» — не безкоштовний обід; це шведський стіл, де кожен потік бере піднос. Якщо додаток запрошує 1000 гостей — потрібен контролер, а не більша кухня.

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

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

Фінансова команда тримала MySQL і MariaDB в різних бізнес‑підрозділах. Інфраструктура була заплутана, але їхня операційна гігієна була надзвичайно доброю. На кожному DB‑хості був стандартний скрипт «захоплення доказів»: він збирав processlist, InnoDB status, ключові лічильники статусу, IO‑статистику і спайс slow logs, і зберігав це з таймштампами.

Одного дня вони отримали сплеск таймаутів. On‑call запустив скрипт двічі з інтервалом у дві хвилини. Другий запуск показав зростання Innodb_log_waits і раптовий стрибок дискового await. Processlist показав багато коротких записів, що чекали commit. Це не була проблема плану запиту; це була проблема латентності запису.

Оскільки вони мали дані, не витрачали час на суперечки про різницю движків. Перевірили журнали ядра й знайшли ресети на одному NVMe. RAID/контролер маскував проблему настільки, що вона виглядала як «повільність бази». Вони швидко перейшли на стендбай і замінили пристрій того ж дня.

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

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

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

1) Симптом: «CPU високий, але запити прості»

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

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

2) Симптом: «Затримка стрибає кожні кілька хвилин, потім відновлюється»

Корінна причина: I/O‑flush шторми, поведінка чекпоінтів або глюки сховища. Також: фонова робота або DDL, що запускається по розкладу.

Виправлення: Корелюйте сплески з iostat await, Innodb_os_log_fsyncs і поведінкою брудних сторінок. Перенесіть пікові операції поза часом навантаження, батчуйте записи і перевірте здоров’я сховища.

3) Симптом: «Відставання репліки росте, але CPU низький»

Корінна причина: застосування SQL на репліці серіалізується через гарячі рядки, великі транзакції, DDL або однониткове застосування. Низький CPU не означає, що все добре; це може означати очікування на I/O або блокування.

Виправлення: Перевірте ріст relay log, довгі транзакції, увімкніть/налаштуйте багатониткову реплікацію там, де можливо, і зменшіть розмір транзакцій на primary.

4) Симптом: «Багато підключень, але Threads_running не дуже високий»

Корінна причина: зміна підключень і ресурсні накладки; додаток не використовує пулінг або health checks надто агресивні. Також: max_connections встановлено високо, щоб «вирішити» помилки, і це створює більшу потенційну площу ураження.

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

5) Симптом: «Дедлоки з’явилися після додавання індексу»

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

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

6) Симптом: «ALTER TABLE зависає вічно»

Корінна причина: metadata lock чекає за довгою транзакцією, часто «безпечне» читання залишене відкритим пулом з’єднань.

Виправлення: Знайдіть блокуючу сесію через processlist/InnoDB status, вбийте або закрийте довгу транзакцію і застосуйте онлайн‑методи зміни схеми з операційними запобіжниками.

7) Симптом: «I/O‑util низький, але await високий»

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

Виправлення: Довіряйте метрикам латентності, перевірте журнали ядра, підтвердіть здоров’я пристрою і розгляньте ізоляцію I/O (виділені томи, кращий QoS) перед настройкою InnoDB.

8) Симптом: «Запити стали повільнішими після оновлення»

Корінна причина: регресії плану оптимізатора, змінені значення за замовчуванням, інша поведінка статистик або різні SQL‑режими. Тут MariaDB і MySQL відрізняються більше, ніж визнають.

Виправлення: Захопіть EXPLAIN‑плани до/після, освіжіть статистики, зафіксуйте критичні запити за допомогою протестованих hint‑ів якщо потрібно, і запускайте відтворення навантаження або тестування під навантаженням, а не лише unit‑тести.

MariaDB vs MySQL: де відмінності насправді болять

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

Інструментація: чи бачите ви, що відбувається?

Performance schema у MySQL 8.0 — сильна перевага для діагностики в продакшні: очікування, стадії, зведення підписів запитів і контенція робляться видимими. У MariaDB інструментація теж є, але зрілість і налаштування за замовчуванням відрізняються в залежності від версії і дистрибутива.

Операційна порада: оберіть одну «золотий шлях» для захоплення доказів на кожен движок. Для MySQL 8.0 спирайтесь на performance_schema і зведення digest. Для MariaDB перевірте, що увімкнено, і покладайтесь на комбінацію статус‑лічильників, InnoDB status, slow logs і таргетованого семплінгу.

Дрейф оптимізатора: той самий запит — різний план

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

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

Поведінка конкурентності: обробка потоків і внутрішня контенція

MariaDB часто обговорюють у зв’язку з поведінкою thread pool в деяких дистрибутивах. MySQL має власні механізми обробки потоків і покращення в різних версіях. У обох випадках тема одна: неконтрольована конкуренція перетворює затримку на лотерею.

Операційна порада: обмежуйте конкуренцію на рівні додатка й на межі бази. Не «вирішуйте» перевантаження підняттям max_connections. Це як розширювати воронку, поки труба засмічена.

Реплікація і кластери: різні інструменти, різні режими відмов

Екосистема MySQL схиляється до GTID, semi‑sync, group replication і managed‑service патернів. MariaDB часто зустрічається з Galera. Обидва рішення можуть бути надійними. Обидва також можуть падати так, що це виглядає як «випадкова повільність», коли реальна проблема — flow control, конфлікти сертифікації або серіалізація застосування.

Операційна порада: визначте, що вам потрібно: масштабування читань, відновлення, multi‑writer чи георозподіл. Потім спроектуйте для цього. Не вмикайте «кластер» як фічу продуктивності; це фіча доступності з витратами на затримку.

Реальність движка зберігання: більшість людей просто запускає InnoDB

Якщо ваше навантаження InnoDB, багато вузьких місць будуть однаковими в обох системах: промахи buffer pool, flush redo log, doublewrite, page flushing і контенція блокувань. Саме тому цей чекліст працює незалежно від бренду.

Операційна порада: якщо хтось каже «MariaDB повільніша» або «MySQL повільніший», запитайте: повільніша в якому класі вузького місця? І де докази?

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

1) Який найшвидший спосіб знайти вузьке місце?

Класифікуйте: CPU‑bound, IO‑bound, lock‑bound чи replication‑bound. Використайте vmstat, iostat -x і SHOW FULL PROCESSLIST перед тим, як чіпати конфіг.

2) З чого починати: slow query log чи performance schema?

Починайте з того, що вже увімкнено й надійно працює. Slow query log має низький поріг входження і працює в обох движках. Performance schema багатший у MySQL 8.0, але треба знати, що увімкнено й який наклад ви готові прийняти.

3) Якщо дискова латентність висока, чи можна просто збільшити buffer pool?

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

4) Чому важливіше «Threads_running» ніж «Threads_connected»?

Connected показує, скільки клієнтів підключено. Running показує, скільки активно використовує CPU або очікує всередині движка. Високі показники running часто корелюють з контенцією й чергуванням.

5) Відставання репліки — це проблема бази чи додатка?

Обидва. Відставання часто викликане формою транзакцій додатка: великі транзакції, гарячі рядки або DDL. Налаштування бази (паралелізм застосування, I/O‑потужність) може допомогти, але не зробить фундаментально послідовне навантаження паралельним.

6) Коли можна змінювати налаштування надійності заради продуктивності?

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

7) Мій запит використовує індекс, але все одно повільний — чому?

«Використовує індекс» все ще може означати скан великого діапазону, випадкові I/O або подальше сортування. Дивіться rows examined, «Using filesort», «Using temporary» і чи індекс відповідає фільтру + порядку.

8) Як уникнути регресій планів під час оновлень MariaDB/MySQL?

Зафіксуйте критичні запити (EXPLAIN‑плани та виконання під репрезентативними даними), програвайте робоче навантаження або навантажувальні тести і порівнюйте топ‑підписи запитів до і після. Розглядайте зміни оптимізатора як і функцію, і ризик.

9) Яка найпоширеніша корінна причина «іноді повільно»?

Черги: fsync‑зависання, очікування блокувань або фонова скидання сторінок. Перервна латентність зазвичай означає, що щось блокує, а не те, що CPU раптом забув як рахувати.

10) Чи варто переходити з MySQL на MariaDB (або навпаки) щоб виправити продуктивність?

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

Наступні кроки, що не зіпсують вам вихідні

Ось практичний план, який я би передав ротації on‑call, що хоче менше загадкових 3 ранку і більш передбачувані системи:

  1. Стандартизувати захоплення доказів: скрипт/runbook, що збирає processlist, InnoDB status, ключові лічильники і статистику I/O хоста з таймштампами.
  2. Увімкнути хоча б одне джерело трасування запитів: slow query log з розумними порогами, плюс захоплення плану для топ‑порушників.
  3. Встановити таксономію вузьких місць: CPU, I/O, блокування, реплікація, пам’ять. Тегуйте інциденти відповідно. Шаблони з’являються швидко.
  4. Виправити форму навантаження: скоротіть транзакції, додайте відсутні композитні індекси, зменшіть join‑fan‑out і обмежте конкурентність на стороні додатка.
  5. Лише потім налаштовувати: підганяйте розміри пам’яті і I/O‑параметри на основі доказів, і робіть це з кроками відкату.

Справжня перевага MariaDB vs MySQL у продуктивності — не секретний параметр. Це операційна ясність. Ви перемагаєте, швидко знаходячи вузьке місце, виправляючи реальну причину і відмовляючись плутати активність з прогресом.

← Попередня
Історія Xeon: як серверні процесори встановлювали правила для всіх
Наступна →
Блокування в MariaDB і SQLite: як уникнути помилок «busy»

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