MySQL vs MariaDB на NVMe: redo-логи, політика flush і правильне налаштування IO

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

Ви купили NVMe. Графіки бенчмарків виглядали як релігійне переживання. Потім прийшов продакшн: спайки p99 латентності, CPU «idle», але запити зависають, і графік схожий на кардіограму. Хтось каже «то просто flush», хтось інший пропонує «збільшити io_capacity», а ще хтось радить «відключити надійність заради продуктивності», ніби це доросла фраза.

Це практичний посібник з експлуатації MySQL або MariaDB на NVMe без гадань. Ми налаштуємо redo-логи, поведінку flush і IO-ємкість командами, які ви можете виконати сьогодні — плюс розглянемо режими відмов, на які нарветесь, якщо не зробите цього.

Факти та історичний контекст (те, що пояснює дивні явища)

  • InnoDB не зростала на флеші. Початкові припущення InnoDB відповідали обертовим дискам: послідовні записи були королем, випадкові — дорогі. NVMe перевертає профіль болю.
  • Буфер doublewrite існує через проблему torn pages. Втрата живлення під час запису 16KB сторінки може залишити напівстарі, напівнові дані. Doublewrite — це «ремінь безпеки».
  • MySQL 8.0 змінила формат redo-логів. «Новий redo» (і робота над атомарними записами) змінили деякі характеристики продуктивності та відновлення в порівнянні зі старими релізами.
  • MariaDB розійшлася у можливостях та значеннях за замовчуванням. Вона зберегла InnoDB (раніше як XtraDB), але поведінка, змінні та деталі реалізації відрізняються настільки, що це важливо при тюнінгу.
  • Group commit — причина, чому можна мати одночасно надійність і пропускну здатність. Багато транзакцій ділять один fsync, коли механізм їх групує. Ваш робочий навантаження визначає, чи отримаєте ви цю вигоду.
  • NVMe — це не «просто швидший диск». Це інший інтерфейс з глибокими чергами й паралелізмом. Однопотокове робоче навантаження з fsync-інтенсивністю все ще може виглядати повільним.
  • Чекпойнтинг — прихований податок. Redo-логи дозволяють InnoDB відкладати запис сторінок, але рахунок приходить у вигляді тиску на чекпойнт і роботи «чистильника сторінок».
  • Блок-слой Linux еволюціонував для флешу. Старі налаштування передбачали SATA/SAS; сучасний NVMe часто потребує планувальника «none» та уважних налаштувань writeback більше, ніж старих трюків з чергою.
  • Хмарний NVMe іноді «виглядає як NVMe». Багато інстансів видають NVMe-пристрої, які насправді підкріплені мережею. Варіація латентності може бути реальною навіть при високій пропускній здатності.

Одна цитата, яку варто приклеїти на стікер, коли вас тягне «просто підкрутити, щоб стало швидко»:
Надія — не стратегія. — генерал Гордон Р. Салліван

Ментальна модель NVMe + InnoDB: що насправді відбувається

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

Три потоки IO, які треба розділяти в голові

  1. Записи redo-логів: відносно послідовні додавання до ib_logfile/redo-файлів. Вони залежать від політики flush і варіації fsync.
  2. Записи сторінок даних: запис брудних 16KB сторінок у tablespace як фонова активність (page cleaners) або при тиску.
  3. Записи doublewrite: додаткові записи для забезпечення crash-safe записів сторінок (якщо не використовувати атомарні записи / налаштування, що змінюють це).

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

Чому p99 латентність стрибає навіть на швидкому NVMe

InnoDB — бухгалтер. Він охоче дозволяє вам «витрачати IO-кредит», буферизуючи брудні сторінки в пам’яті. Коли треба погасити борг — бо redo майже заповнений,
або відсоток брудних сторінок занадто високий, або чекпойнт має просунутись — він може змусити foreground-потоки допомогти з flush.
Саме там виникають затримки: транзакції чекають на flush журналу, запис сторінки або і те, й інше.

Жарт №1: NVMe робить погану політику flush швидшою, як дати малюку еспресо — речі відбуваються швидше, але не краще.

MySQL vs MariaDB: що відрізняється щодо redo/flush/IO capacity

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

Відмінності в конфігурації redo-логів

  • MySQL 8.0: використовує innodb_redo_log_capacity (сучасний перемикач) і керує файлами redo внутрішньо. Багато старих гайдів досі говорять про innodb_log_file_size; це — старий світ.
  • MariaDB: зазвичай використовує innodb_log_file_size та innodb_log_files_in_group (залежно від версії). Може не мати такої ж абстракції ємності redo.

IO capacity і фонові потоки

Семантика innodb_io_capacity та innodb_io_capacity_max схожа за духом, але не очікуйте ідентичної поведінки під тиском.
Налаштування page cleaner та flush neighbor також можуть відрізнятись.

Атомарні записи та поведінка doublewrite

Тут «NVMe-тюнінг» стає реальним. Деякі розгортання покладаються на файлову систему + гарантії пристрою (атомарні 16KB записи, поведение DAX-подібне або функції пристрою),
щоб зменшити потребу в doublewrite. Багато хто цього не має. А в хмарі NVMe часто не поводиться як ваш лабораторний SSD.
Трактуйте doublewrite як обов’язковий, поки не доведете, що весь стек витримує torn pages.

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

Redo-логи на NVMe: розмір, розмітка та динаміка чекпойнтів

Redo-логи — ваш амортизатор ударів. Більша ємність redo дозволяє InnoDB накопичувати більше брудної роботи й виконувати flush більш плавно.
Але «більше — краще» перетворюється на «відновлення займає вічність», якщо переборщити, і це може приховати проблему flush до 3 ранку.

Що дають redo-логи

  • Вони розв’язують коміт і запис сторінок (переважно). Коміт записує redo; сторінки можуть записатись пізніше.
  • Вони згладжують випадкові патерни записів у відносно послідовні додавання в журнал.
  • Вони визначають бюджет чекпойнта. Якщо чекпойнт не може просунутися, ви зависнете.

Реальність щодо NVMe: redo — про варіацію латентності, а не пропускну здатність

Багато NVMe-пристроїв можуть робити шалені об’єми пропускної здатності, але варіація латентності fsync під тиском — те, що вбиває.
Шлях redo-логу чутливий до:

  • поведінки кешу запису пристрою та семантики FUA/flush,
  • режиму журналювання файлової системи,
  • завантаження kernel writeback,
  • фонових сплесків записів сторінок, що конкурують за ті самі черги пристрою.

Розмір redo: практичні рекомендації

На сучасних системах недостатній redo — це самостворена рана. Якщо ваша ємність redo мала, ви постійно робитимете чекпойнти,
і чистильники сторінок будуть трясти систему. Це перетворює «швидкий NVMe» на «чому інколи коміт займає 40ms».

Зробіть так:

  • Розмірюйте redo так, щоб пікові записні сплески не встрявали у стан «log full».
  • Перевірте очікуваний час відновлення. Великий redo означає більше журналу для сканування під час аварійного відновлення.
  • Спостерігайте вік чекпойнту та відсоток брудних сторінок; налаштовуйте, щоб уникати пилкоподібних патернів.

Коли redo занадто великий

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

Політика flush: органи надійності та їхня вартість

Є два види людей у БД: ті, хто вже втрачав дані, і ті, хто ще не встиг. Політика flush вирішує, в якій ви групі.

innodb_flush_log_at_trx_commit: великий важіль

Це налаштування вирішує, коли InnoDB скидає redo на надійне сховище. Поширені значення:

  • 1: запис і fsync журналу при кожному коміті. Найкраща надійність; найвища чутливість до латентності fsync.
  • 2: запис при коміті, fsync раз на секунду. Можливі втрати до ~1 секунди транзакцій при аварії/втраті живлення.
  • 0: flush раз на секунду; ще більше потенційне вікно втрат.

Моя думка: ставте 1 для більшості важливих продакшенів і вкладайте в те, щоб fsync був передбачуваним.
Використовуйте 2 лише якщо бізнес явно приймає вікно втрат і це задокументовано в runbook.

sync_binlog: не забувайте про надійність реплікації

Якщо ви використовуєте бінлог (реплікація, відновлення до точки часу, CDC), sync_binlog теж впливає на надійність.
Поширена «упс» ситуація — надійний redo, але ненадійний binlog, після чого аварія викликає дивні явища при реплікації або прогалини в PITR.

Файлова система та опції монтування важливі більше, ніж більшість «база-данихних» налаштувань

На Linux ext4 і XFS поводяться по-різному під fsync. Режим журналювання і barrier мають значення.
Якщо ви використовуєте хмарні томи, блоковий пристрій може «брехати» про семантику кешу в спосіб, який «працює» до дня, коли перестає.
Ось чому SREи вчаться не довіряти графікам, що виглядають надто гладко.

Жарт №2: Відключати fsync заради продуктивності — як зняти детектор диму, бо він голосно спрацьовує.

Правильне налаштування IO capacity: io_capacity, фонові IO і брудні сторінки

innodb_io_capacity — це не «наскільки швидкий ваш диск». Це підказка InnoDB про те, наскільки агресивно він повинен флашити у фоні.
Якщо встановити занадто низько, брудні сторінки накопичуватимуться і flush стане сплескоподібним. Занадто високо — ви створите постійний тиск запису,
який конкуруватиме з читаннями і підвищуватиме латентність.

Мета: рівномірний flush, а не героїчні сплески

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

Як вибирати стартові значення

  • Почніть з реалістичної бази. NVMe може робити десятки тисяч IOPS, але патерни flush InnoDB — не сирі 4k випадкові записи.
  • Виміряйте фактичні стабільні записні IOPS і латентність під навантаженням бази, а не на порожній машині з бенчмарком.
  • Використовуйте innodb_io_capacity_max як «стелю для сплесків», а не як робочий план на день.

Управління брудними сторінками — провісник

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

Linux + NVMe налаштування, які справді мають значення (і що — порожня ритуальщина)

Існує цілий жанр «NVMe тюнінгу», що фактично — cargo cult. Коробка швидка; вузьке місце часто в семантиці flush,
контенції черг, плануванні CPU або поведінці файлової системи. Проте кілька перевірок у Linux постійно варті вашого часу.

Планувальник IO: зазвичай none для NVMe

Для NVMe багаточерговий блочний шар ядра означає, що традиційні планувальники часто не допомагають. Багато дистрибутивів уже за замовчуванням ставлять none.
Підтверджуйте, не припускайте.

Writeback та dirty-коефіцієнти: уникайте синхронізованих штормів

Kernel dirty page writeback може синхронізуватися з InnoDB flush і породити періодичну конгестію.
Ви не «вирішуєте» це випадковими sysctl-змінами; виміряйте, чи корелюють сплески writeback з застоями БД,
а потім налаштовуйте обережно.

Частота CPU та переривання: тихе підривання

NVMe + висока QD може бути вимогливим до CPU. Якщо ваш CPU агресивно знижує частоту або переривання погано прив’язані,
ваш «швидкий диск» стає дорогим нагрівачем. Шляхи, чутливі до латентності fsync, особливо ненавидять джиттер.

TRIM/discard: ставтеся з повагою

Онлайн discard може викликати сплески латентності на деяких пристроях або стеках. Багато операторів віддають перевагу періодичному fstrim
під час вікон обслуговування. Якість прошивки NVMe різниться, і ви не перетюните поганий день прошивки.

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

Це реальні завдання, які ви можете виконати на Linux-хості з MySQL або MariaDB. Кожне містить, що означає вивід
і яке рішення з нього випливає. Не виконуйте їх усі одночасно в продакшні. Обирайте ті, що відповідають вашим симптомам.

Завдання 1: Підтвердіть, чи у вас MySQL чи MariaDB (і версію)

cr0x@server:~$ mysql --version
mysql  Ver 8.0.36 for Linux on x86_64 (MySQL Community Server - GPL)

Що це означає: У вас MySQL 8.0, отже розмір redo ймовірно використовує innodb_redo_log_capacity, а не старі перемикачі.

Рішення: Використовуйте імена змінних та лічильники MySQL 8.0; не застосовуйте налаштування, специфічні для MariaDB.

Завдання 2: Зафіксуйте ключові налаштування надійності InnoDB

cr0x@server:~$ mysql -Nse "SHOW VARIABLES WHERE Variable_name IN ('innodb_flush_log_at_trx_commit','sync_binlog','innodb_doublewrite','innodb_flush_method');"
innodb_doublewrite	ON
innodb_flush_log_at_trx_commit	1
innodb_flush_method	O_DIRECT
sync_binlog	1

Що це означає: Це позиція «надійно за замовчуванням» (redo і binlog синхронізовані). O_DIRECT уникає подвійного кешування.

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

Завдання 3: Перевірте змінні розміру redo (MySQL 8.0)

cr0x@server:~$ mysql -Nse "SHOW VARIABLES LIKE 'innodb_redo_log_capacity';"
innodb_redo_log_capacity	2147483648

Що це означає: Ємність redo — 2GiB. Для інтенсивних записів це може бути замало і викликати тиск чекпойнта.

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

Завдання 4: Перевірте змінні розміру redo (стиль MariaDB)

cr0x@server:~$ mysql -Nse "SHOW VARIABLES WHERE Variable_name IN ('innodb_log_file_size','innodb_log_files_in_group');"
innodb_log_file_size	268435456
innodb_log_files_in_group	2

Що це означає: Загальна ємність redo приблизно ~512MiB. Це часто замало для OLTP на NVMe, де пишуться пікові навантаження.

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

Завдання 5: Перевірте відсоток брудних сторінок і тиск буфер-пулу

cr0x@server:~$ mysql -Nse "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_%';"
Innodb_buffer_pool_pages_data	1048576
Innodb_buffer_pool_pages_dirty	196608
Innodb_buffer_pool_pages_free	1024
Innodb_buffer_pool_pages_total	1050624

Що це означає: Брудні сторінки ~18.7% (196608/1050624). Вільні сторінки майже немає, буфер-пул повністю використаний.

Рішення: Якщо брудні сторінки ростуть під стабільним навантаженням — підвищуйте фонове флашення (innodb_io_capacity) або усувайте IO-контенцію. Якщо вони сильно коливаються — зменшіть сплески (збільшення redo, налаштування flush).

Завдання 6: Перевірте поведінку чекпойнтів через InnoDB engine status

cr0x@server:~$ mysql -Nse "SHOW ENGINE INNODB STATUS\G" | sed -n '1,120p'
*************************** 1. row ***************************
  Type: InnoDB
  Name: 
Status:
=====================================
2025-12-31 12:14:51 0x7f3c2c0c4700 INNODB MONITOR OUTPUT
=====================================
Log sequence number          879112345678
Log flushed up to            879112300000
Pages flushed up to          879110900000
Last checkpoint at           879110900000

Що це означає: LSN просувається (записи), flush відстає, і чекпойнт — це «Pages flushed up to». Якщо Log sequence number далеко попереду Last checkpoint, ви накопичуєте вік чекпойнта.

Рішення: Якщо вік чекпойнта росте до точок зависання — збільшуйте ємність redo і/або піднімайте IO capacity; також перевіряйте сплески латентності fsync та накладні витрати doublewrite.

Завдання 7: Заміряйте очікування, пов’язані з fsync, у Performance Schema (MySQL)

cr0x@server:~$ mysql -Nse "SELECT event_name, count_star, ROUND(sum_timer_wait/1000000000000,2) AS total_s FROM performance_schema.events_waits_summary_global_by_event_name WHERE event_name LIKE 'wait/io/file/innodb/innodb_log_file%' ORDER BY sum_timer_wait DESC LIMIT 5;"
wait/io/file/innodb/innodb_log_file	1289345	842.11

Що це означає: Багато часу витрачається на очікування IO redo-логів. Це часто відповідає вашій латентності коміту.

Рішення: Корелюйте з p99 латентністю комітів. Якщо висока — фокусуйтеся на шляху flush: варіація латентності пристрою, журналювання файлової системи, конгестія ядра.

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

cr0x@server:~$ mysql -Nse "SHOW GLOBAL STATUS WHERE Variable_name IN ('Innodb_log_waits','Innodb_log_write_requests','Innodb_os_log_fsyncs','Innodb_os_log_written');"
Innodb_log_waits	413
Innodb_log_write_requests	98234567
Innodb_os_log_fsyncs	4512390
Innodb_os_log_written	9876543210

Що це означає: Innodb_log_waits ненульове — транзакції чекали через обмеження буфера журналу/простору redo.

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

Завдання 9: Підтвердіть модель NVMe-пристрою, прошивку та PCIe-лінк (апаратна санітарна перевірка)

cr0x@server:~$ sudo nvme list
Node             SN                   Model                                    Namespace Usage                      Format           FW Rev
/dev/nvme0n1     S6X...               SAMSUNG MZVL21T0HCLR-00B00                1         900.19  GB /   1.00  TB  512   B +  0 B  GXA7401Q

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

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

Завдання 10: Перевірте планувальник IO для NVMe

cr0x@server:~$ cat /sys/block/nvme0n1/queue/scheduler
[none] mq-deadline kyber bfq

Що це означає: Планувальник — none, що часто правильно для NVMe.

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

Завдання 11: Перевірте файлову систему та опції монтування (тут живуть семантики flush)

cr0x@server:~$ findmnt -no SOURCE,FSTYPE,OPTIONS /var/lib/mysql
/dev/nvme0n1p2 ext4 rw,relatime,errors=remount-ro,data=ordered

Що це означає: ext4 з data=ordered. Це впливає на поведінку журналу і може впливати на вартість fsync.

Рішення: Якщо латентність fsync нестабільна — тестуйте альтернативні файлові системи або опції монтування в staging; не міняйте опції продакшн необережно.

Завдання 12: Перевірте налаштування kernel dirty writeback

cr0x@server:~$ sysctl vm.dirty_background_ratio vm.dirty_ratio vm.dirty_expire_centisecs vm.dirty_writeback_centisecs
vm.dirty_background_ratio = 10
vm.dirty_ratio = 20
vm.dirty_expire_centisecs = 3000
vm.dirty_writeback_centisecs = 500

Що це означає: Ядро почне фоновий writeback приблизно при 10% брудних сторінок і буде дроселювати при ~20%. Ці значення можуть бути прийнятні або синхронізуватися погано з InnoDB flush.

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

Завдання 13: Спостерігайте реальний час латентності IO і чергування

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

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           6.12    0.00    2.11    3.95    0.00   87.82

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz  aqu-sz  %util
nvme0n1         820.0  52500.0     0.0    0.0    1.20    64.0   1600.0  78000.0     0.0    0.0   8.70    48.8   15.2   92.0

Що це означає: Записи мають ~8.7ms await з високим %util. Це не «швидкий NVMe» в сенсі, якого хочуть ваші коміти.

Рішення: Якщо латентність комітів корелює з w_await, зменшіть сплески записів (розмір redo, налаштування IO capacity) і дослідіть насичення пристрою або «гучних сусідів».

Завдання 14: Визначте головних споживачів IO MySQL на рівні ОС

cr0x@server:~$ sudo pidstat -d 1 3 -p $(pidof mysqld)
Linux 6.1.0 (server) 	12/31/2025 	_x86_64_	(32 CPU)

12:16:03      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
12:16:04      999     24187  12000.00  54000.00      0.00      52  mysqld
12:16:05      999     24187  11500.00  61000.00      0.00      61  mysqld

Що це означає: MySQL — генератор записів. Добре. Якщо інший процес багато пише, він може відібрати IO і зруйнувати хвостову латентність.

Рішення: Якщо є IO-конкуренція — ізолюйте MySQL (виділений том, cgroups/io.max, окремий пристрій або вбити «гучного сусіда»).

Завдання 15: Перевірте живі налаштування InnoDB IO capacity

cr0x@server:~$ mysql -Nse "SHOW VARIABLES WHERE Variable_name IN ('innodb_io_capacity','innodb_io_capacity_max','innodb_page_cleaners');"
innodb_io_capacity	200
innodb_io_capacity_max	2000
innodb_page_cleaners	4

Що це означає: innodb_io_capacity=200 — значення ери обертових дисків. На NVMe воно часто занадто низьке, що призводить до накопичення брудних сторінок і майбутніх застоїв.

Рішення: Збільшуйте поступово (наприклад, 1000–5000 залежно від навантаження та пристрою), слідкуйте за брудними сторінками, віком чекпойнта та латентністю читань. Не піднімайте до 20000, бо бачили це на форумі.

Завдання 16: Підтвердіть налаштування doublewrite та, пов’язані з атомарними записами (керування ризиками)

cr0x@server:~$ mysql -Nse "SHOW VARIABLES LIKE 'innodb_doublewrite';"
innodb_doublewrite	ON

Що це означає: Doublewrite увімкнено, що захищає від torn pages.

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

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

Коли латентність стрибає й усі дивляться на одні й ті самі дашборди, потрібен короткий шлях до «що це насправді?» Ось послідовність, яка швидко знаходить вузьке місце в більшості інцидентів на NVMe-backed MySQL/MariaDB.

Перше: це латентність fsync redo чи тиск на flush сторінок?

  • Перевірте латентність комітів і log waits: Innodb_log_waits, Performance Schema redo waits і час комітів в додатку.
  • Перевірте iostat -x на сплески w_await і високий %util.

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

Друге: чи чистильники сторінок відстають?

  • Слідкуйте за відсотком брудних сторінок з часом.
  • Перевіряйте статус InnoDB на предмет росту віку чекпойнта та активності флашу.
  • Підтвердіть, що innodb_io_capacity не встановлено як у 2009 році.

Якщо cleaners відстають: підвищуйте IO capacity поступово, розгляньте збільшення redo capacity і перевірте write amplification від doublewrite + файлової системи.

Третє: чи це латентність читань через конкуренцію з записами?

  • Якщо читання сповільнюються під час flush-штормів — у вас контенція IO черг.
  • Перевірте, чи фоновий writeback ядра співпадає з застоями БД (налаштування kernel dirty writeback).

Якщо латентність читань корелює з записами: зменште сплескоподібність flush (redo/IO capacity), ізолюйте IO і перевірте планувальник та поведінку CPU.

Четверте (коли «дивно»): доведіть, чи NVMe — справжній NVMe

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

Три корпоративні міні-історії з реального життя

1) Інцидент через неправильне припущення: «NVMe означає, що fsync дешевий»

Компанія з фінтечною спрямованістю мігрувала критичний MySQL-кластер з SATA SSD на локальний NVMe на нових хостах. Команда очікувала: менша латентність, вища пропускна здатність, менше IO-інцидентів. Вони отримали перші два в медіані, а останній став гіршим.

Симптом був класичний: p95 — в нормі, а p99.9 періодично йшов у прірву. Потоки додатка накопичувалися в очікуванні комітів. On-call дивився на CPU і бачив багато idle. Пропускна здатність сховища була далеко від меж пристрою. «Отже, це не диск», — сказав хтось, і в кімнаті перестали заперечувати, бо графіки переконливі.

Хибне припущення полягало в тому, що NVMe автоматично робить fsync передбачуваним. Насправді їхня файлова система + опції монтування
і внутрішні фонові процедури прошивки пристрою створювали періодичні fsync-стали під сталим тиском запису.
Коміти були затримані цими стрибками, бо вони працювали з innodb_flush_log_at_trx_commit=1 (правильно) і не мали запасу, коли fsync-джиттер вдарив.

Виправлення не було «вимкнути надійність». Вони виміряли wait-події redo fsync, зв’язали їх зі сплесками iostat, протестували іншу конфігурацію файлової системи в staging і додали запасу ємності redo, щоб чекпойнти були менш сплескоподібні.
Хвостова латентність стабілізувалася. Сюрприз був у тому, що «найшвидше» сховище давало найнепередбачуванішу латентність, поки стек не налаштований для передбачуваності.

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

Інша компанія мала MariaDB-кластер для сервісу з інтенсивними записами. Вони побачили, що брудні сторінки ростуть під час пікових навантажень, і вирішили, що горить флашинг.
Хтось встановив innodb_io_capacity у значення, яке виглядало розумно у порівнянні з синтетичним NVMe-бенчмарком, і підняв його під час вікна техобслуговування.

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

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

Вони відкотили зміни на нижчий io_capacity, потім заново налаштовували поступово, слідкуючи одночасно за читаннями та брудними сторінками.
Правильний підхід — помірна базова швидкість флашу з розумним максимумом і невелике збільшення redo capacity, щоб уникнути панічних чекпойнтів.
Урок: «більше флашу» ≠ «кращий флаш».

3) Нудна, але правильна практика, що врятувала день: валідація часу відновлення після збою

SaaS-провайдер використовував MySQL з відносно великою ємністю redo, бо навантаження було сплескоподібним. Це робило систему плавною під навантаженням.
Але вони робили непопулярну річ: регулярно тестували час відновлення після збою в staging з обсягом даних і рівнем використання redo, схожим на продакшн.

Під час події дата-центру primary впав різко і перезапустився. План відмови передбачав певний бюджет часу на рестарт.
Оскільки вони протестували, вони знали, чого чекати, і вже налаштували redo capacity так, щоб він укладався в вікно відновлення.
Реплікація наздогнала чисто, бо налаштування надійності binlog відповідали загальній позиції щодо надійності.

Коли інші команди сперечалися в чаті, чи відновлювати з бекапу або підвищувати репліку, ця команда діяла за runbook:
підтвердити прогрес відновлення redo, моніторити стадії recovery і не давати трафіку, поки двигун не повідомив консистентний стан.

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

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

1) Симптом: спайки p99 латентності комітів, пропускна здатність в нормі

Корінь: варіація латентності fsync redo (прошивка пристрою, журналювання файлової системи, конгестія writeback або IO-контенція).

Виправлення: виміряйте redo wait-події; перевірте iostat -x на сплески write await; ізолюйте IO; перевірте опції файлової системи; уникайте конкурентних записувачів на тому самому томі.

2) Симптом: періодичні «штормові» застояні хвилі кожні кілька хвилин

Корінь: тиск чекпойнта і сплескоподібні флаші через недостатню ємність redo і/або низьке innodb_io_capacity.

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

3) Симптом: читання сповільнюються під час росту записів, хоча NVMe не на межі пропускної здатності

Корінь: контенція IO черг від фонового флашу або write amplification через doublewrite; читання застрягають за записами.

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

4) Симптом: «log waits» ростуть під нормальним трафіком

Корінь: простір redo обмежений; чекпойнт не може просунутися; тиск буфера журналу; іноді занадто мала ємність redo.

Виправлення: збільшіть ємність redo; забезпечте, щоб cleaners могли флашити стабільно; перевірте, чи doublewrite і файлова система не множать IO без потреби.

5) Симптом: після «прискорення» зміною надійності реплікація/PITR стає ненадійною

Корінь: невідповідна позиція надійності між redo і binlog (наприклад, innodb_flush_log_at_trx_commit=2, але sync_binlog=0) або хибні припущення щодо консистентності після збою.

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

6) Симптом: NVMe показує високий %util, але MySQL не виконує багато запитів

Корінь: фоновий flush, doublewrite або writeback файлової системи; або інший процес багато пише.

Виправлення: використайте pidstat -d щоб знайти записувачів; перевірте брудні сторінки InnoDB; перегляньте налаштування kernel dirty writeback; розгляньте перенесення не-DB навантажень з тому.

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

Корінь: інша модель/прошивка NVMe, інша хмарна поведінка, інша конкуренція або фонові завдання (бекапи, компресія, ETL).

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

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

Покроково: базова діагностика перед будь-яким тюнінгом

  1. Занотуйте версію MySQL/MariaDB і ключові змінні: політика flush, розмір redo, IO capacity, doublewrite, надійність binlog.
  2. Зберіть 10 хвилин iostat -x під репрезентативним навантаженням.
  3. Зробіть знімок статусу InnoDB на початку і в кінці цього вікна (рух LSN і чекпойнтів).
  4. Зафіксуйте тренди відсотка брудних сторінок.
  5. Підтвердіть файлову систему та опції монтування для datadir.
  6. Підтвердіть відсутність конкурентних великих записувачів на тому самому пристрої.

Покроково: стабілізація redo і чекпойнтів

  1. Якщо ємність redo мала — збільшіть її, щоб зменшити чиркання чекпойнтів (планове техвікно, якщо потрібно).
  2. Після зміни перевірте час відновлення під контрольованим рестартом у staging; не змінюйте ємкість redo без знання вартості рестарту.
  3. Слідкуйте за Innodb_log_waits і віком чекпойнта; вони мають знижуватися.

Покроково: налаштування IO capacity без шкоди для читань

  1. Збільшуйте innodb_io_capacity малими кроками.
  2. Після кожного кроку дивіться: брудні сторінки, латентність читань, write await і навантаження CPU.
  3. Встановіть innodb_io_capacity_max для дозволених сплесків, але тримайте його в межах.
  4. Зупиніться, коли брудні сторінки стабілізуються і p99 читань не погіршується.

Покроково: вирішуйте питання надійності як дорослий

  1. Запишіть допустиме вікно втрати даних (0 секунд? 1 секунда? більше?). Отримайте підпис від тієї людини, що несе відповідальність за наслідки.
  2. Узгодьте innodb_flush_log_at_trx_commit і sync_binlog з цим рішенням.
  3. Тестуйте поведінку після збою і recovery у staging: аварійна зупинка/вмикання, потім відновлення і перевірки консистентності.

FAQ

1) Чи завжди ставити innodb_flush_log_at_trx_commit=2 на NVMe?

Ні. NVMe може робити fsync швидким, але «швидкий» — не те саме, що «передбачуваний». Використовуйте 2 тільки якщо бізнес приймає втрату до ~1 секунди комітів при аварії.

2) Який хороший розмір redo на NVMe?

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

3) Чи завжди збільшення redo покращує продуктивність?

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

4) Чи можна відключити буфер doublewrite на NVMe?

Тільки якщо ви можете довести, що весь ваш стек запобігає torn pages (пристрій + файлова система + конфігурація) і ви протестували відновлення після збою. В іншому випадку тримайте ввімкненим і налаштовуйте навколо нього.

5) Чому innodb_io_capacity досі 200 в багатьох конфігах?

Бо конфіги живуть довше за покоління апаратури. 200 був виправданим для обертових дисків. На NVMe це може бути генератором застоїв.

6) Мій NVMe показує 90% util, але низьку пропускну здатність. Чи це нормально?

Так, якщо домінують малі синхронні записи (fsync) або латентні IO. Високе %util може відображати чергування та час очікування, а не тільки пропускну здатність.

7) Чи варто відокремлювати redo-логи на інший NVMe-пристрій?

Іноді — особливо якщо запис сторінок «з’їдає» fsync redo. Але це додає операційної складності і може призвести до нових проблем (планування ємності, домени відмов пристроїв). Спочатку виміряйте.

8) MySQL vs MariaDB: хто «кращий» на NVMe?

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

9) Чому тюнінг у staging не сходиться з продакшном?

Через конкуренцію, фонові роботи, «гучних сусідів» і прошивку пристрою. Хвостова латентність — це спорт продакшну, якщо тільки ваш staging не справді копія продакшн-парадигми.

Висновок: наступні кроки, які можна виконати цього тижня

Якщо ви хочете, щоб NVMe в продакшні відчувалося як магія, не ганяйтеся за піковими IOPS. Переслідуйте передбачувану латентність fsync і рівномірний flush.
Розмір redo — ваш амортизатор. Політика flush — ваш контракт ризику. IO capacity — як ви не даєте двигуну панічно погашати борги.

  1. Запустіть базові завдання: зафіксуйте налаштування надійності, розмір redo, брудні сторінки, iostat, опції файлової системи.
  2. Зробіть швидку діагностику під час реального спайку і класифікуйте: fsync redo, тиск чекпойнта чи IO-конкуренція.
  3. Якщо redo малий і чекпойнтинг сплескоподібний — заплануйте збільшення ємності redo з тестом часу відновлення.
  4. Підвищуйте innodb_io_capacity поступово, поки брудні сторінки не стабілізуються без погіршення p99 читань.
  5. Запишіть рішення щодо надійності (innodb_flush_log_at_trx_commit, sync_binlog) і припиніть трактувати їх як «кнопки продуктивності».

Зробіть це — і ваш NVMe буде не просто швидким. Він буде нудним. А нудність — те, що вам потрібно о 2-й ночі.

← Попередня
Proxmox ZFS — DEGRADED: заміна диска без побічних наслідків
Наступна →
Плани виконання в MariaDB і PostgreSQL: знаходьте реальне вузьке місце, а не симптоми

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