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

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

О 02:13 тривога не звертає уваги на те, який логотип на вашій базі даних. Їй важливо, що оформлення замовлення таймаутиться,
репліки відстають, а хтось «щойно підвищив max_connections», бо звучало як корисне рішення.

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

Що ви насправді обираєте (це не бенчмарк)

Дебати MariaDB проти Percona Server зазвичай подають як перегони: хто швидший? Це неправильне питання.
У продакшені ви обираєте пакет дефолтів, операційних інструментів, сумісність і форму болю, яку відчуєте в масштабі.

Обидва — MariaDB і Percona Server — починалися як «MySQL, але краще». Сьогодні це різні екосистеми. Percona Server (для MySQL)
тримається ближче до upstream MySQL і відслідковує його мажорні випуски. MariaDB має власну гілку релізів і з часом суттєво розійшлася —
іноді корисно, іноді так, що дивує команди, які думали «це майже MySQL».

Якщо у вас OLTP-навантаження і воно вже досить непогано налаштоване, великі виграші від продуктивності рідко приходять від «бренду сервера».
Вони приходять від:

  • гігієни запитів та індексів (так, досі)
  • розміру buffer pool і контролю витоку сторінок
  • поведінки redo/undo і налаштувань флашу, узгоджених зі сховищем
  • управління підключеннями та планування потоків при вибуховому навантаженні
  • топології реплікації та налаштувань надійності, які відповідають бізнесу
  • спостережуваності, яка не перетворюється на навантаження

Гілка, яку ви обираєте, має найбільше значення, коли потрібна конкретна функція (thread pool, інструментація, інтеграція з бекуп-тулом),
коли хочете зменшити операційний ризик або зберегти сумісність з MySQL для сторонніх застосунків. «Було на 12% швидше в блоґовому тесті»
рідко є достатньою причиною, бо вузьке місце зазвичай у іншому місці.

Цікаві факти та історичний контекст (коротко і чітко)

Трохи контексту допоможе простіше розкодовувати маркетинг:

  1. MariaDB створили 2009 року після того, як Oracle придбала Sun (і, відповідно, MySQL). Назва — відсилання до доньки Monty Widenius, як і «MySQL» був пов’язаний з «My».
  2. Percona побудувала бізнес на ліквідації пожеж продуктивності MySQL перед тим, як випустити Percona Server як дистрибутив з практичними патчами і інструментарієм.
  3. Percona Server історично постачав «XtraDB» (варіант InnoDB з фокусом на продуктивність) до того, як InnoDB від MySQL наздогнав за багатьма можливостями; брендинг лишається, але апстрімні покращення зменшили розрив.
  4. Mariabackup з’явився тому, що XtraBackup від Percona став де-факто стандартом для гарячих фізичних бекапів у світі MySQL; пізніше MariaDB надала еквівалент, узгоджений зі своєю кодовою базою.
  5. Оптимізатор і набір фіч MariaDB значно відходили від MySQL протягом років; сумісність з MySQL — не статична обіцянка, а рухома ціль.
  6. Performance Schema стала з «опціонального оверхеду» реальністю за замовчуванням у дистрибутивах MySQL; операційне питання стало «як правильно вибірково збирати», а не «чи інструментувати».
  7. Планування потоків (thread pool) регулярно повертається в обговорення, бо модель з одним підключенням на потік поводиться погано під хвилею підключень; pool — практичне виправлення, коли застосунки некоректні.
  8. Сучасні SSD і NVMe змінили підхід до тюнінгу InnoDB: випадкові IOPS стали відносно дешевими, але fsync і write amplification все ще кусають, особливо з налаштуваннями надійності та doublewrite.

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

1) «Краща продуктивність одразу після встановлення»

Іноді правда, але часто це марно. «Одразу після встановлення» залежить від дефолтів ОС, файлової системи, обмежень cgroup, масштабування частоти CPU і того, чи дистрибутив постачається з консервативними конфігами.

Що може бути реальним:

  • Більш розумні дефолти для продакшну (buffer pool, IO-потоки, метод флашу) можуть помітно допомогти командам, які ніколи не налаштовували MySQL правильно.
  • Наявність thread pool (залежно від видання/версії) може стабілізувати продуктивність при великій кількості підключень.
  • Додаткові важелі спостережуваності можуть зменшити «невідомі невідомості» під час інцидентів.

Що — маркетинг:

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

2) «Покращений InnoDB / XtraDB означає швидші записи»

Історично патчі XtraDB від Percona мали значення. Сьогодні upstream MySQL InnoDB вже поглинув багато покращень.
Залишкові відмінності зазвичай стосуються інструментації, налаштувань і операційної поведінки на краях.

Реальні виграші усе ще існують, але вони умовні:

  • Для write-heavy навантажень покращена поведінка флашу та розумніший розмір redo логів допомагають — незалежно від форку.
  • Для IO-залежних навантажень більше користі від правильного налаштування IO-ємності та уникнення подвійного буферування, ніж від вибору форка.
  • Для середовищ з інтенсивною реплікацією важить те, наскільки ефективно ви можете діагностувати, тротлингувати і відновлювати — інструменти мають значення.

3) «Thread pool вирішує проблеми конкурентності»

Thread pool — реальна річ. Він може перетворити шторм підключень із «смерть сервера» на «повільніше, але живе». Але це не магія. Це контроль допуску. Він не виправляє повільні запити, погані індекси або одну “гарячу” рядок, яку всі оновлюють.

Thread pool найбільш цінний коли:

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

4) «Краща інструментація»

Екосистема Percona історично робила акцент на видимості: аналіз повільних запитів, digest-зведення, безпечні дефолти для операцій. MariaDB має власну інструментацію і лічильники статусу, які в деяких середовищах цілком адекватні.

Пастка — думати, що «більше метрик» означає «більше продуктивності». Інструментація може додавати накладні витрати, особливо трасування на рівні виразів чи очікувань при високому QPS. Увімкніть її цілеспрямовано. Здійснюйте семплінг. Ротуйте. Автоматизуйте.

Одна переказана ідея від Jim Gray (піонера надійності і транзакційної обробки): лікуйте відмови як норму; проектуйте так, щоб система залишалася правильною, коли вони трапляються.
Налаштування продуктивності, що компрометують коректність — це повільне відкатне падіння.

5) «Сумісність як drop-in заміна»

Саме тут форки на практиці розходяться. Percona Server загалом ближчий до поведінки upstream MySQL. MariaDB вносила фічі і зміни, які можуть дивувати застосунки, що залежать від конкретних SQL-режимів, оптимізаторних особливостей, семантики реплікації або системних таблиць.

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

Жарт №1: Бенчмарки — як прилади в орендованій машині: ви можете відчувати себе швидким, коли їдете по схилу з вимкненим двигуном.

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

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

Перший: це CPU, IO, блокування чи планування підключень?

  • CPU bound: високий користувацький CPU, низький IO wait, запити з великою витратою циклів, погані індекси, важкі сортування, погані join-операції.
  • IO bound: високий IO wait, велика латентність fsync, інтенсивні пропуски buffer pool, зупинки на чистці брудних сторінок, тиск на redo.
  • Lock bound: багато потоків «Waiting for lock», гарячі рядки, довгі транзакції, metadata locks.
  • Connection bound: забагато активних з’єднань, контекстні переключення, крах планування потоків, backlog в accept-черзі.

Другий: чи болить основне навантаження чи «помічники»?

  • потоки реплікації (SQL/applier) відстають
  • бекап-завдання та snapshot-и б’ють по IO
  • моніторинг скрейпить занадто агресивно
  • DDL-операції або online schema change неправильно тротляться

Третій: що змінилося?

  • деплої (зміна форми запитів)
  • зміни конфігурації (налаштування флашу, розміри буферів, конкарентність потоків)
  • ріст даних (індекс більше не вміщується в пам’ять)
  • події інфраструктури (шумний сусід, дивна прошивка диска)

Мета в перші 10 хвилин — не «виправити назавжди». Це класифікувати вузьке місце, зупинити кровотечу і уникнути каскадного відмови (реплікаційного лаґу, штормів ретраїв, натовпу підключень).

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

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

Припущення: хост Linux, systemd і локальний клієнт. Підлаштовуйте імена сервісів, якщо пакування відрізняється.

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

cr0x@server:~$ mysql -e "SELECT VERSION() AS version, @@version_comment AS comment, @@version_compile_machine AS arch;"
+-------------------------+------------------------------+------+
| version                 | comment                      | arch |
+-------------------------+------------------------------+------+
| 8.0.36-28               | Percona Server (GPL), Release 28 | x86_64 |
+-------------------------+------------------------------+------+

Що це означає: Тепер ви знаєте точний форк і лінію збірки. Багато гайдів з тюнінгу залежать від версії.

Рішення: Підбирайте тюнінг і очікування функцій під цю версію. Не застосовуйте MariaDB-специфічні ручки до Percona або навпаки.

Завдання 2: Перевірте, чи ви CPU bound чи IO bound (швидкий огляд)

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
 2  0      0 521240  81232 8123456    0    0    12    55  980 2100 22  6 69  3  0
 6  3      0 498120  79012 8011200    0    0   820  2400 1250 9900 28  9 41 22  0

Що це означає: «wa» (IO wait) підскакує до 22% — це натяк на тиск на IO, а не виключно на CPU.

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

Завдання 3: Замірте латентність сховища з боку ОС

cr0x@server:~$ iostat -x 1 3
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          18.21    0.00    6.11   19.44    0.00   56.24

Device            r/s     w/s   r_await   w_await  aqu-sz  %util
nvme0n1         120.0  980.0     1.20     9.80    3.40   92.0

Що це означає: Записи чекають ~10мс і зайнятість висока. Це реальне вузьке місце, якщо ваше навантаження записо-інтенсивне.

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

Завдання 4: Перевірте активні потоки та основні очікування всередині MySQL/MariaDB

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Threads_running'; SHOW GLOBAL STATUS LIKE 'Threads_connected';"
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| Threads_running | 184   |
+-----------------+-------+
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_connected | 620   |
+-------------------+-------+

Що це означає: 184 running threads — багато для більшості машин. Якщо CPU не завантажений, можливо блокування/IO або трешинг.

Рішення: Розгляньте thread pool (якщо доступний) або виправлення пулінгу підключень у застосунку. Тимчасово ідентифікуйте топ-блокери.

Завдання 5: Знайдіть найгірші запити за сумарним часом (slow log digest)

cr0x@server:~$ pt-query-digest /var/log/mysql/mysql-slow.log --limit=3
#  1  45%  120s  0.2s  600x  SELECT orders ... WHERE user_id = ?
#  2  22%   60s  1.0s   60x  UPDATE inventory ... WHERE sku = ?
#  3  15%   40s  0.1s  400x  SELECT cart_items ... JOIN products ...

Що це означає: Запит #1 повільний не через виконання одного разу, а через обсяг. Запит #2 повільний на виконання і ймовірно викликає contention.

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

Завдання 6: Підтвердіть розмір buffer pool і проксі hit rate

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads'; SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';"
+-------------------------+------------+
| Variable_name           | Value      |
+-------------------------+------------+
| innodb_buffer_pool_size | 25769803776|
+-------------------------+------------+
+--------------------------+----------+
| Variable_name            | Value    |
+--------------------------+----------+
| Innodb_buffer_pool_reads | 18422012 |
+--------------------------+----------+
+----------------------------------+------------+
| Variable_name                    | Value      |
+----------------------------------+------------+
| Innodb_buffer_pool_read_requests | 9871120031 |
+----------------------------------+------------+

Що це означає: Читання з диска є, але потрібен тренд по часу для оцінки. Якщо під час сплесків reads різко ростуть, ви втрачаєте кеш.

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

Завдання 7: Перевірте тиск на redo логи і поведінку чекпоінтів

cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | sed -n '1,120p'
Log sequence number          812345678901
Log flushed up to            812345670000
Last checkpoint at           812300000000
0 pending log flushes, 0 pending chkp writes

Що це означає: Якщо «Log flushed up to» відстає далеко за LSN під навантаженням, fsync відстає. Якщо чекпоінт значно відстає, можливі зупинки на брудних сторінках.

Рішення: Налаштуйте розмір redo логів і параметри флашу обережно; перевірте латентність сховища. Також дивіться відсоток брудних сторінок і політику флашингу.

Завдання 8: Виявіть очікування на блокування та блокуючі команди

cr0x@server:~$ mysql -e "SHOW PROCESSLIST;" | head -n 15
Id  User  Host      db   Command Time State                    Info
91  app   10.0.2.5  shop Query   42   Waiting for table metadata lock ALTER TABLE orders ADD COLUMN ...
104 app   10.0.2.9  shop Query   41   Updating                 UPDATE inventory SET qty=qty-1 WHERE sku='X'

Що це означає: DDL тримає або чекає metadata locks і може блокувати прикладні запити, залежно від таймінгу.

Рішення: Припиніть виконувати несподівані DDL на праймарі під час піку. Використовуйте інструменти online schema change і плануйте вікна обслуговування з тротлінгом за навантаженням.

Завдання 9: Підтвердіть здоров’я реплікації (primary/replica)

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

Що це означає: Репліка здорова, але відстає. Це питання пропускної здатності або ефективності apply, а не розриву з’єднання.

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

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

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'max_connections'; SHOW GLOBAL STATUS LIKE 'Max_used_connections'; SHOW GLOBAL STATUS LIKE 'Aborted_connects';"
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 2000  |
+-----------------+-------+
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| Max_used_connections | 1987  |
+----------------------+-------+
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| Aborted_connects | 9321  |
+------------------+-------+

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

Рішення: Виправте пулінг і таймаути в застосунку. Розгляньте thread pool. Накладіть backpressure на периферії. Слідкуйте за 2000 підключень як за баг-репортом.

Завдання 11: Підтвердіть налаштування durability (і чи «швидкість» — це просто менше fsync)

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit'; SHOW VARIABLES LIKE 'sync_binlog';"
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| innodb_flush_log_at_trx_commit | 2   |
+------------------------------+-------+
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sync_binlog   | 0     |
+---------------+-------+

Що це означає: Це «швидше», бо менш надійно. Після збою ви можете втратити транзакції і binlog-події.

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

Завдання 12: Виявте тиск тимчасових таблиць і сортування (прихований диск IO)

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables'; SHOW GLOBAL STATUS LIKE 'Created_tmp_tables'; SHOW GLOBAL STATUS LIKE 'Sort_merge_passes';"
+-------------------------+--------+
| Variable_name           | Value  |
+-------------------------+--------+
| Created_tmp_disk_tables | 120322 |
+-------------------------+--------+
+---------------------+--------+
| Variable_name       | Value  |
+---------------------+--------+
| Created_tmp_tables  | 890441 |
+---------------------+--------+
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Sort_merge_passes | 9921  |
+-------------------+-------+

Що це означає: Багато тимчасових таблиць спливає на диск, сортування робить merge-проходи. Це CPU і IO, на які ви не закладали бюджет.

Рішення: Спочатку тримайтеся за тюнінг запитів і індексів. Потім перегляньте tmp_table_size/max_heap_table_size і сортувальні буфери з обережністю (великі буфери × багато потоків = вибухове споживання RAM).

Завдання 13: Перевірте, чи страждаєте від throttling брудних сторінок

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_dirty'; SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_total';"
+--------------------------------+--------+
| Variable_name                  | Value  |
+--------------------------------+--------+
| Innodb_buffer_pool_pages_dirty | 412000 |
+--------------------------------+--------+
+-------------------------------+--------+
| Variable_name                 | Value  |
+-------------------------------+--------+
| Innodb_buffer_pool_pages_total| 1572864|
+-------------------------------+--------+

Що це означає: Приблизно 26% брудних сторінок. Якщо ви бачите зупинки і це наближається до конфігурованого максимуму, можна вдаритися у флаш-шторм.

Рішення: Перевірте innodb_max_dirty_pages_pct і налаштування IO-ємності. Якщо сховище не витримує, тюнінг лише перерозподіляє страждання.

Завдання 14: Підтвердіть налаштування binlog і GTID для операційної здоровості

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'gtid_mode'; SHOW VARIABLES LIKE 'enforce_gtid_consistency'; SHOW VARIABLES LIKE 'log_bin';"
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| gtid_mode                | ON    |
+--------------------------+-------+
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| enforce_gtid_consistency | ON    |
+--------------------------+-------+
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin       | ON    |
+---------------+-------+

Що це означає: У вас є передумови для пристойних робочих процедур відновлення з реплікацією.

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

Завдання 15: Перевірте, чи у вас є свап (мовчазний вбивця продуктивності)

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:            64Gi        58Gi       1.2Gi       1.0Gi       4.8Gi       2.0Gi
Swap:           8Gi        1.5Gi       6.5Gi

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

Рішення: Зменшіть тиск пам’яті (буфери, підключення, пам’ять на потік) і розгляньте відключення swap лише якщо у вас сувора дисципліна OOM і ємності.

Завдання 16: Порівняйте ключові конфігураційні відмінності між двома серверами (міграційна перевірка)

cr0x@server:~$ mysql -e "SHOW VARIABLES" | egrep '^(innodb_flush_method|innodb_io_capacity|innodb_log_file_size|max_connections|thread_handling)\s'
innodb_flush_method	O_DIRECT
innodb_io_capacity	2000
innodb_log_file_size	1073741824
max_connections	2000
thread_handling	one-thread-per-connection

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

Рішення: Стандартизуйтесь по конфігах там, де можливо, а потім тестуйте відмінності навмисно. Уникайте «сніжинок» баз даних.

Де налаштування продуктивності справді провалюються в продакшені

Вибір форку не врятує від математики IO

Математика IO безжальна і постійна. Якщо у вас write-heavy навантаження з durable commits (fsync на commit, синхронізація binlog), латентність сховища задає нижню межу латентності commit. «Швидший форк» може зекономити CPU, але не перетворить 10ms fsync на 1ms.

Справжній важіль — узгодження durability і сховища:

  • NVMe з передбачуваною латентністю fsync
  • окремі томи (або принаймні різні домени навантаження) для даних і логів, коли це допомагає
  • правильний innodb_flush_method, щоб уникнути подвійного кешування
  • розмір redo логів, щоб зменшити трясіння чекпоінтів

Thread pool може стабілізувати, але й приховувати гниль

Thread pool — як ремінь безпеки. Добре, коли машина врізається у щось. Але він не заміна за уміння водити.

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

Інструментація корисна лише якщо ви можете дозволити її

Багато продакшн-інцидентів — це «борг спостережуваності», накопичений: ніхто не вмикав slow logs, ніхто не зберігав query digests, і тепер всі хочуть повний sampling під час пожежі. Увімкнення всього під час вогню може стати самим вогнем.

Практичний підхід:

  • тримаєте slow query logging доступним і з ротацією
  • увімкніть performance instrumentation на сталому рівні
  • знайте, як тимчасово підвищити sampling і повернути назад

Проблеми сумісності часто проявляються як «проблеми продуктивності»

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

Жарт №2: «Ми ввімкнули всі опції продуктивності» — це базу даних еквівалент «ми вирішили шум прибрати, знявши пожежний датчик».

Три корпоративні міні-історії (анонімізовано, болісно правдоподібні)

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

Середня SaaS-компанія працювала на сторонньому застосунку «сертифікованому для MySQL». Вони були на старішій community MySQL і хотіли кращу спостережуваність і підтримку. Хтось запропонував MariaDB як «drop-in» заміну. Встановлення пройшло чисто, реплікація піднялася, дашборди виглядали нормально, і міграцію оголосили успішною.

Через два тижні, після підвищення трафіку, латентність на певному ендпоінті, який завжди був на межі, підскочила. План запиту змінився. Не драматично — лише настільки, щоб порядок join-ів змінився і вторинний індекс став використовуватися не так, як раніше. Команда зробила те, що зазвичай роблять: збільшили buffer pool, збільшили розміри tmp table, додали CPU. Сплески стали рідшими, але хвіст став гіршим.

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

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

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

Фінтех-команда хотіла нижчу латентність commit. У них був пристойний NVMe, але p99 записів все ще болював під піками. Добра інженерна ідея змінила налаштування durability: innodb_flush_log_at_trx_commit=2 і sync_binlog=0. Бенчмарки виглядали чудово. Графіки стали гарніші. Усі пішли додому.

Через місяць під час планового обслуговування трапився kernel panic. Праймар повернувся, репліки перепідключилися, і застосунок працював здебільшого — крім підмножини транзакцій, які зникли. Не «відкотилися». Зникли. Binlog їх не мав, redo лог підтверджував commits без настільки ж сильної персистентності, як бізнес очікував, і погодження стало уїк-ендовим випробуванням у контрольованому горі.

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

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

Міні-історія 3: Сумна, але правильна практика, що врятувала ситуацію

Маркетплейс-компанія запускала Percona Server з репліками по зонах. Нічого фантастичного. Вони робили одну нецікаву, але послідовну річ: щотижневі тести відновлення з фізичних бекапів у staging, плюс періодичну перевірку, що відновлена інстанція може приєднатися до реплікації з GTID.

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

Команда не дебатувала годинами. Вони підвищили здорову репліку, ізолювали зламану і відновили її з найсвіжішого бекапу. Процедура відновлення була задокументована, відрепетирувана і автоматизована настільки, що спрацювала під тиском. Інцидент став уроком про продуктивність, а не катастрофою для компанії.

Отак: «налаштування продуктивності» — це цікаво, але операційна практика тримає світло вмикненим, коли налаштування йде не так.

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

1) Симптом: p99 латентності сплески кожні кілька хвилин

Корінна причина: чекпоінт-/флаш-шторм (брудні сторінки накопичуються, потім флаш блокує foreground роботу), часто підсилюється повільним сховищем або неправильно встановленою IO-ємністю.

Виправлення: виміряйте латентність сховища; налаштуйте innodb_io_capacity та innodb_io_capacity_max відповідно до можливостей пристрою; правильно розмірюйте redo логи; уникайте важкого DDL під піковим навантаженням.

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

Корінна причина: contention на блокуваннях (рядкові блокування, гарячі індекси, metadata locks), або колапс планування потоків від надмірної кількості підключень.

Виправлення: ідентифікуйте блокери через processlist/performance schema; скоротіть транзакції; додайте або підлаштуйте індекси; виправте пулінг підключень; розгляньте thread pool для контролю допуску.

3) Симптом: репліки відстають непередбачувано після пікових годин

Корінна причина: вибухові binlog від довгих транзакцій; застосування репліки обмежено одноманітним потоком або ресурсним contention; важкі читання на репліці забирають ресурси apply.

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

4) Симптом: «база повільна» лише під час бекапів

Корінна причина: IO-конфлікт бекапів або ампліфікація snapshot-ів; інструмент бекапу конкурує за кеш і диск.

Виправлення: тротуйте бекап; плануйте на непік; використовуйте репліки для бекапів; перевірте, що метод бекапу сумісний з файловою системою і сховищем.

5) Симптом: використання пам’яті зростає, потім OOM або свап-шторм

Корінна причина: пам’ять на підключення множиться через багато сесій (sort buffers, tmp tables, join buffers), плюс перетягнуті кеші.

Виправлення: обмежте підключення; правильно налаштуйте буфери на потік; використайте thread pool; вимірюйте реальний слід пам’яті; уникайте рефлексу «просто збільшити tmp_table_size».

6) Симптом: плани запитів регресували після міграції

Корінна причина: відмінності оптимізатора, зміна статистики, різні дефолти SQL-режимів або налаштувань двигуна між форками/версіями.

Виправлення: робіть explain regression тести на реальних даних; закріплюйте індекси там, де потрібно; вирівняйте SQL-моди; не вважайте «drop-in» за гарантію без тестування навантаження.

7) Симптом: високий час fsync, commits повільні

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

Виправлення: виміряйте await пристрою; переконайтеся, що flush method відповідний (часто O_DIRECT); розділіть логи, якщо треба; апгрейдьте сховище або свідомо змініть durability.

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

Чекліст A: Вибір між MariaDB і Percona Server для продакшну

  1. Вимога сумісності: Якщо вендор вимагає підтримку версії MySQL, надавайте перевагу Percona Server у тій мажорній лінії MySQL.
  2. Операційний інструментарій: Якщо ви вже використовуєте потоки Percona toolkit (digests, аналіз) і хочете мінімального розходження поведінки — Percona Server менш болісний шлях.
  3. Потреба в фічах: Якщо потрібні специфічні фічі MariaDB (певні режими реплікації, опції движка) — приймайте компроміс сумісності і тестуйте сильніше.
  4. Навички команди: Якщо команда дебагує з Performance Schema і має знання upstream MySQL, Percona Server краща карта. Якщо команда вже експлуатує MariaDB у масштабі — не змінюйте через ідеологію.
  5. Шлях оновлення: Обирайте форк, ритм оновлень якого ви реально зможете підтримувати. Стагновані бази — місце, де «налаштування продуктивності» помирають.

Чекліст B: Безпечний тюнінг продуктивності в будь-якому форку (робити в порядку)

  1. Увімкніть slow query logging з розумними порогами, ротуйте логи і виробіть щотижневу звичку аналізу digest.
  2. Підтвердіть розмір buffer pool і перевірте, чи вміщується робочий набір; не гадати — вимірювати read churn за часом.
  3. Перевірте латентність і зайнятість сховища під навантаженням; лагодьте IO перед «тюнінгом бази».
  4. Встановіть durability свідомо; задокументуйте компроміси і узгодьте їх з бізнес-ризиком.
  5. Виправляйте топ-запити за сумарним часом, а не за его. Часті «швидкі» запити можуть бути реальною статтею витрат.
  6. Виключіть довгі транзакції і несподівані DDL під піком. Вони спричиняють блокування і реплікаційну патофізіологію.
  7. Контролюйте підключення: пулінг, таймаути, circuit breakers; розгляньте thread pooling як стабілізатор.
  8. Тестуйте навантаження з наборами даних, схожими на продакшн; поведінка оптимізатора змінюється зі скіюваністю даних.

Чекліст C: План міграції (MariaDB ↔ Percona Server) без ризику гри в рулетку

  1. Інвентар залежностей: SQL-моди, плагіни автентифікації, налаштування реплікації, інструменти бекапу і вимоги вендора.
  2. Diff конфігів: витягніть SHOW VARIABLES з обох; знайдіть форк-специфічні ручки; прибирайте невідомі налаштування.
  3. Відтворення навантаження: використовуйте staging зі схемою продакшну + репрезентативними даними; проганяйте захоплені робочі навантаження, якщо можливо.
  4. План відкату: протестована процедура відновлення або підвищення репліки; потренуйте, задокументуйте.
  5. Cutover через реплікацію: створіть репліку на новому движку, перевірте контрольні суми, а потім поетапно перемикайте трафік у контрольованому вікні.
  6. Після cutover: обмежте max connections, увімкніть slow logs, перевірте реплікацію і слідкуйте за латентністю commit та очікуваннями блокувань.

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

1) Хто швидший: MariaDB чи Percona Server?

Жоден надійно в загальному розумінні. Для багатьох OLTP-навантажень вузьким місцем є IO, блокування або погані запити, а не форк. Обирайте за сумісністю, експлуатаційністю і потрібними фічами.

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

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

3) Чи MariaDB все ще є drop-in заміною для MySQL?

Іноді, для простих застосунків і при обережному підбиранні версій. Але чим довше строк, тим більше значення має відхилення. Трактуйте «drop-in» як гіпотезу, яку потрібно тестувати на реальному навантаженні і explain-regression, а не як обіцянку.

4) Чи thread pool вирішить мою проблему з високим max_connections?

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

5) Чи «налаштування продуктивності» здебільшого про зміну InnoDB?

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

6) Як зрозуміти, чи покращення продуктивності прийшло через зниження durability?

Перевірте innodb_flush_log_at_trx_commit і sync_binlog. Якщо вони послаблені, ваш бенчмарк може виглядати чудово, бо ви не платите за персистентність на кожному коміті.

7) Чи слід увімкнути всі інструменти Performance Schema для дебагу?

Не за замовчуванням. Увімкніть те, що потрібно, семплінгуйте відповідально і знайте накладні витрати. Добра практика — базова конфігурація, що завжди ввімкнена, і задокументований «інцидентний режим», який можна коротко включити.

8) Який найпростіший спосіб покращити реплікаційний лаґ?

Усуньте довгі транзакції на праймарі, зменшіть сплески записів і дайте реплікам достатньо IO/CPU. Потім налаштуйте паралельне застосування, якщо ваша версія це підтримує і навантаження виграє.

9) Якщо я IO bound, чи марна зміна форку?

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

10) Що стандартизувати між середовищами, щоб уникнути «в стаґінгу було швидше»?

Лінія версії MySQL/MariaDB, змінні конфігурації, схема і індекси, форма датасету і клас сховища. Особливо клас сховища. Стаґінг на швидкому локальному NVMe і продакшн на мережевих дисках — класичний урок смирення.

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

  1. Проганяйте швидкий план діагностики один раз під час звичного піка і збережіть базові виводи: vmstat, iostat, ключові рядки статусу InnoDB і digest повільних запитів.
  2. Виберіть топ-3 запити за сумарним часом із slow logs і виправте їх індексами або зменшенням частоти. Впровадьте ці зміни перед тим, як чіпати екзотичні серверні ручки.
  3. Аудит налаштувань durability і зафіксуйте бізнес-рішення. Якщо ви випадково працюєте з послабленою durability, ви живете у борг.
  4. Обріжте і контролюйте підключення: узгодьте пулами застосунків кількість з CPU і IO-бюджетом; не розглядайте max_connections як стратегію масштабування.
  5. Прийміть рішення щодо форку на основі обмежень: спочатку сумісність з вендором і ритм оновлень; «швидше» — наприкінці списку причин.
  6. Відрепетируйте відновлення з бекапів. Якщо ви не можете спокійно відновити — у вас не бекапи, а дороге надіяння.

Якщо потрібно грубе, але конкретне порадження: обирайте Percona Server, коли вам потрібна upstream MySQL сумісність і операційна передбачуваність.
Обирайте MariaDB, коли вам явно потрібна екосистема/фічі MariaDB і ви готові тестувати та нести відповідальність за відмінності.
У будь-якому випадку найшвидша база даних — та, у якої менше поганих запитів, менше несподіваних транзакцій і сховище, яке не бреше щодо латентності.

← Попередня
Налаштування ZFS для пулів тільки на HDD: швидкість без SSD
Наступна →
AVX-512: чому одні його люблять, а інші бояться

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