Полноформатная адаптация для Habr по мотивам статьи Adrien Obernesser «MariaDB 12.3 – Binlog Inside InnoDB».
Коротко для ленивых
В MariaDB 12.3 binlog можно хранить внутри InnoDB через
binlog_storage_engine=innodb.Главный эффект: вместо двух
fsync()на commit остаётся один, поэтому на write-heavy нагрузке резко растут TPS и снижается tail latency.В тестах из статьи прирост на полном durability-профиле составил примерно
2.4x–3.3x.Backup, restore и ресинк реплик становятся проще, потому что binlog и данные теперь консистентны на уровне одного механизма хранения.
Цена за это: обязателен GTID, Galera пока не поддерживается, а
innodb_log_file_sizeнужно подбирать внимательнее из-за роста объёма redo.Если у вас обычная схема
primary + async replicaна InnoDB, эту возможность точно стоит хотя бы протестировать.
С MariaDB и MySQL в продакшене я регулярно сталкиваюсь с одной и той же практической проблемой: бинарный лог нужен почти всем, но платить за него лишней задержкой на каждом commit хочется далеко не всегда. Особенно это заметно на облачных дисках, где каждая лишняя синхронная запись быстро превращается в реальный потолок по TPS и неприятный хвост latency.
Поэтому новость про binlog_storage_engine=innodb в MariaDB 12.3 я воспринял не как очередную «точечную оптимизацию», а как действительно важное архитектурное изменение. В этой версии binlog можно хранить прямо внутри InnoDB, используя его WAL. Ниже рассмотрим, что именно изменилось, почему это даёт такой заметный прирост производительности, какие ограничения есть уже сейчас и где эту возможность действительно имеет смысл включать.
Почему классический binlog так дорого обходится
Любой DBA, который администрировал MariaDB или MySQL под реальной нагрузкой, хорошо знает этот компромисс: binlog нужен для репликации, восстановления и аудита, но за него почти всегда приходится платить производительностью.
Причина в классическом пути фиксации транзакции. Каждая commit-операция проходит через границу двухфазного коммита между InnoDB и бинарным логом. На практике это означает два независимых способа поддержания долговечности (durability) и, в стандартной конфигурации, два последовательных вызова fsync():
один для binlog;
второй для redo/WAL InnoDB.
И это происходит независимо от того, как вы настроили innodb_flush_log_at_trx_commit: бинарный лог всё равно живёт отдельно от InnoDB и требует собственной синхронизации.
Отсюда возникают вполне знакомые эффекты:
на каждом commit нужно согласовать два разных механизма поддержания долговечности;
после падения базы данных (crash), механизм восстановления (recovery) должен согласовать состояние InnoDB и состояние binlog;
при
sync_binlog=1иinnodb_flush_log_at_trx_commit=1вы всегда платите за два вызоваfsync();при высокой конкуренции транзакций всё это особенно больно бьёт по пропускной способности системы, увеличивая задержку.
На быстрых локальных NVMe это ещё можно пережить. На облачных дисках, где задержка синхронной записи измеряется миллисекундами, проблема становится очень заметной.
MariaDB 12.3 предлагает на это архитектурный ответ. Изменение проходит по задаче MDEV-34705, автор реализации — Kristian Nielsen. Суть в том, что binlog больше не обязан существовать как отдельный плоский файл на уровне сервера: он может жить внутри InnoDB и использовать тот же Write-Ahead Log, что и сами данные.
Это важно подчеркнуть: речь не о небольшой оптимизации, а о перепроектировании всего процесса сохранения данных (commit pipeline).
Как выглядел старый commit path
Если упростить, традиционная фиксация транзакции выглядела так:
BEGIN transaction → InnoDB: XA-подготовка, запись в журнал (undo/redo log) → Binlog: запись события в файл binlog → Binlog: синхронизация binlog-файла с диском — fsync() [sync_binlog=1] → InnoDB: commit (фиксация в журнале) → InnoDB: синхронизация файла InnoDB с диском — fsync() [innodb_flush_log_at_trx_commit=1] COMMIT
Главная проблема здесь в том, что binlog и InnoDB — это два разных файла и две разные точки подтверждения записи. InnoDB не может окончательно завершить commit, пока не удостоверится, что событие уже надёжно сохранено в binlog.
Если сервер падает в неудачный момент — например, между записью binlog и финальным commit внутри InnoDB — восстановление после сбоя должно пройтись по binlog и найти подготовленные, но не завершённые транзакции. Это даёт накладные расходы в двух местах:
в режиме стабильной работы, когда мы платим дополнительной задержкой на commit;
при восстановлении (recovery), когда приходится дополнительно сканировать и согласовывать состояние.
Group commit частично помогает: несколько транзакций можно синхронизировать вместе. Но базовая цена 2PC никуда не исчезает.
Что меняется в MariaDB 12.3
С включённым binlog_storage_engine=innodb бинарный лог перестаёт быть отдельным плоским файлом. Теперь он хранится внутри табличных пространств InnoDB с расширением .ibb, а его долговечность обеспечивается собственным redo/WAL InnoDB.
В этом режиме путь фиксации упрощается до следующего:
BEGIN transaction → InnoDB: запись в журнал (undo/redo log) + запись события в binlog → InnoDB: commit (один вызов fsync(), если innodb_flush_log_at_trx_commit=1) COMMIT
То есть двухфазный коммит между InnoDB и binlog исчезает. Изменения данных и записи бинлога становятся атомарными: они либо попадают в WAL вместе, либо не попадают вовсе.
Именно это даёт два очень серьёзных эффекта.
1. Значения параметра innodb_flush_log_at_trx_commit 0 и 2 перестают быть опасными для binlog-consistency
В старой модели ослабленные режимы flush были рискованными с точки зрения репликации: после падения процесса или ОС binlog и InnoDB могли разойтись. Данные в InnoDB уже зафиксированы, а binlog при этом неполный, или наоборот.
В новой схеме binlog находится внутри того же пути записи, что и данные. Значит, после падения системы процесс восстановления InnoDB восстанавливает консистентное состояние сразу и для таблиц, и для бинлога.
Важно понимать нюанс: это не делает значение 0 магически «безопасным» с точки зрения потери последних данных при сбое питания. Но это устраняет с��арую проблему рассинхронизации между binlog и InnoDB.
2. При innodb_flush_log_at_trx_commit=1 один fsync() заменяет два
Это самый важный прикладной выигрыш. Для наиболее типичного продакшн-профиля теперь нужен один синхронный flush WAL вместо двух. На практике именно поэтому в такой конфигурации виден такой заметный рост TPS и снижение задержки.
Дополнительно улучшаются условия для group commit: binlog больше не остаётся отдельным bottleneck.
Как теперь выглядят файлы binlog
При новом режиме MariaDB создаёт binlog-файлы с расширением .ibb. Они предвыделяются размером max_binlog_size, по умолчанию это 1 ГБ.
Для сравнения, классический FILE binlog обычно выглядит так:
$ ls -lah /var/lib/mysql/binlog/ drwx------ 2 mysql mysql 4.0K #binlog_cache_files -rw-rw---- 1 mysql mysql 1.9K mysqld-bin.000001 -rw-rw---- 1 mysql mysql 4.0K mysqld-bin.000001.idx -rw-rw---- 1 mysql mysql 1.4K mysqld-bin.000002 -rw-rw---- 1 mysql mysql 0 mysqld-bin.000002.idx -rw-rw---- 1 mysql mysql 80 mysqld-bin.index
В режиме InnoDB binlog каталог выглядит уже так:
$ ls -lah /var/lib/mysql/binlog/ -rw-rw---- 1 mysql mysql 1.0G binlog-000000.ibb -rw-rw---- 1 mysql mysql 1.0G binlog-000001.ibb
Разница заметна сразу.
Во-первых, сразу создаются файлы заранее заданного размера. Во-вторых, пропадают привычные .index, .idx и .state файлы. Состояние GTID пишется внутрь самого binlog как параметр транзакции с интервалом, задаваемым через --innodb-binlog-state-interval.
В результате восстановление позиции GTID выполняется не через внешние индексные файлы, а через сканирование binlog от последней зафиксированной транзакции вперёд.
Есть и ещё одна интересная деталь: в выводе утилиты mariadb-binlog для InnoDB binlog события могут показываться с end_log_pos 0, поскольку позиционирование ведёт сам InnoDB. У классического FILE binlog, напротив, видны реальные смещения в байтах.
Кроме того, транзакции в журнале могут быть распределены по разным файлам. Для пользователя это обычно прозрачно: mariadb-binlog сам соберёт всё корректно, если передать файлы в правильном порядке.
Что это меняет в backup и restore
Здесь начинается уже не только улучшение производительности, но и сильное упрощение обслуживания.
mariadb-backup теперь может забирать binlog как часть транзакционно согласованного состояния. То есть backup получает данные и binlog атомарно, без отдельной ручной сверки позиций.
Это означает следующее:
binlog включается в backup по умолчанию;
сервер не нужно блокировать на время отдельного копирования binlog;
восстановленный backup можно быстрее превратить в реплику;
отпадает значительная часть старой ручной возни с позициями.
Практический смысл здесь огромный: операции восстановления и пересоздания реплик становятся заметно менее хрупкими.
Операционная сторона: GTID, ресинк реплик и split-brain
На мой взгляд, это одна из самых важных частей всей истории, потому что реальные DBA-выигрыши здесь не только в TPS, но и в снижении количества ручных процедур.
Если вы используете binlog_storage_engine=innodb, то нормально жить дальше получится только с GTID. Старую схему file/offset здесь нужно считать прошлым этапом.
Что на самом деле означает MASTER_USE_GTID = slave_pos
Это место регулярно вызывает путаницу у тех, кто впервые внимательно читает документацию MariaDB.
В записи:
CHANGE MASTER TO MASTER_USE_GTID = slave_pos;
slave_pos — это не значение, которое вы должны подставить вручную. Это литеральный режим работы, один из трёх допустимых вариантов:
MASTER_USE_GTID = no MASTER_USE_GTID = slave_pos MASTER_USE_GTID = current_pos
Смысл slave_pos такой: сервер должен взять стартовую позицию для репликации из собственной системной переменной @@gtid_slave_pos.
То есть DBA передаёт не сами данные, а инструкцию. Фактическая позиция определяется движком и восстанавливается автоматически.
Как выглядела синхронизация реплики раньше
В классической FILE-схеме процедура обычно выглядела так:
Снимаем свежий backup через
mariadb-backup.Читаем из backup файл
xtrabackup_binlog_info.Получаем что-то вроде
binlog.000042 position 198472910.Восстанавливаем backup на реплике.
Вручную прописываем:
CHANGE MASTER TO MASTER_LOG_FILE = 'binlog.000042', MASTER_LOG_POS = 198472910; START SLAVE;
Проблема в том, что здесь два независимых источника истины: состояние InnoDB и состояние binlog. И если позиция оказалась смещена хотя бы на одну транзакцию, можно получить либо повторное применение событий, либо их пропуск. Худший вариант — тихий drift без немедленной ошибки.
История с MDEV-21611 хорошо показывает, что такие рассогласования не являются чисто теоретическими.
Как выглядит синхронизация с InnoDB binlog и GTID
Здесь схема радикально проще:
Снимаем backup.
Вместе с данными в него уже попадают
.ibbbinlog-файлы.Восстанавливаем backup на реплике.
@@gtid_slave_posвосстанавливается автоматически.Настраиваем репликацию так:
CHANGE MASTER TO MASTER_HOST = 'primary', MASTER_USER = 'replicator', MASTER_PASSWORD = 'replpass', MASTER_USE_GTID = slave_pos, MASTER_DEMOTE_TO_SLAVE = 1; START SLAVE;
И всё. Ни имени binlog-файла, ни byte offset DBA руками больше не указывает.
С практической точки зрения это большое улучшение: резервная копия уже содержит консистентную точку, а реплика сама объявляет мастеру свой GTID-набор и получает дальнейший поток событий ровно оттуда, где нужно.
Почему это особенно важно при split-brain
Настоящая ценность GTID и нового binlog особенно хорошо проявляется в неприятных аварийных сценариях.
Представим, что primary и replica временно потеряли связь.
Primary успел зафиксировать GTID с
0-1-1по0-1-10042.Replica отставала и успела получить только до
0-1-10039.Затем replica временно стала primary и приняла ещё три локальные записи.
В результате формально у обоих серверов может оказаться диапазон до 0-1-10042, но содержимое последних транзакций будет разным.
В старой FILE-схеме такое расхождение часто приходилось расследовать вручную: просматривать binlog двух сторон, искать точку расхождения, а затем принимать решение, можно ли безопасно откатиться или проще пересоздать реплику с нуля.
С GTID картинка становится гораздо более явной.
На проблемной реплике:
SELECT @@gtid_slave_pos;
На актуальном primary:
SELECT @@gtid_binlog_pos;
Если включён gtid_strict_mode = ON, MariaDB не позволит молча применить GTID с другим содержимым. Вместо тихой порчи данных вы получите явную ошибку.
Для ��инхронизации достаточно вернуть последнюю подтверждённо корректную позицию:
STOP SLAVE; RESET SLAVE; SET GLOBAL gtid_slave_pos = '0-1-10039'; START SLAVE;
После этого сервер снова начнёт получать события с нужной точки без ручной арифметики по byte offset.
Для чего нужен MASTER_DEMOTE_TO_SLAVE=1
Это очень полезный механизм для бывших primary.
Если сервер какое-то время принимал локальные записи, его @@gtid_binlog_pos уже содержит GTID, которых текущий primary может не знать. Если в этот момент просто использовать MASTER_USE_GTID = slave_pos, можно получить дыру между тем, что сервер уже когда-то локально записал, и тем, откуда он будет считать себя репликой.
MASTER_DEMOTE_TO_SLAVE = 1 решает это автоматически: MariaDB берёт объединение @@gtid_slave_pos и @@gtid_binlog_pos, записывает его как новую стартовую позицию и уже потом подключается к primary.
По-человечески это читается так: «если у меня были локальные записи, учти их при старте репликации, чтобы я не попытался заново применить уже имеющееся состояние».
В результате типовое руководство по восстановлению после split-brain сильно упрощается:
STOP SLAVE; SET GLOBAL gtid_slave_pos = '<last_known_good_gtid>'; CHANGE MASTER TO MASTER_USE_GTID = slave_pos, MASTER_DEMOTE_TO_SLAVE = 1; START SLAVE;
Именно здесь новая архитектура начинает экономить не только миллисекунды, но и реальные часы инженерного времени.
А что с Galera
Здесь есть важное ограничение: в текущем виде Galera с InnoDB binlog не работает.
Причина упирается не в «недопиленную галочку», а в архитектуру wsrep.
Galera-кластер реплицирует не через binlog. Он использует write set protocol:
транзакция доходит до стадии prepare;
из неё извлекается набор изменений;
этот write set рассылается узлам;
проходит сертификация конфликтов;
затем commit выполняется согласованно на всех узлах.
Упрощённая схема такая:
Client writes to Node A → InnoDB: prepare transaction → wsrep: extract write set from transaction → wsrep: broadcast write set to all nodes → wsrep: certification round → wsrep: apply write set on all nodes simultaneously → InnoDB: commit on all nodes → binlog written AFTER commit
Проблема в том, что wsrep historically встраивается между prepare и commit. А новая схема InnoDB binlog как раз делает prepare и commit одной атомарной операцией без отдельной точки остановки.
То есть wsrep теряет место, куда он может встроить сертификацию.
Поэтому для поддержки Galera нужно не просто «разрешить фичу», а переинтегрировать wsrep на другом уровне API движка. Это серьёзная инженерная задача. В MDEV-34705 это отмечено как будущее направление, но в 12.3 поддержки нет.
Что это означает на практике
сами Galera-ноды пока не могут использовать
binlog_storage_engine=innodb;async-реплики, которые висят за Galera-нодой, остаются рабочей схемой, если сама Galera-нода продолжает использовать FILE binlog;
смешивать storage engine binlog в одной репликационной цепочке «как попало» не получится: формат определяет источник.
Матрица решений по топологиям
Для быстрого ориентира:
Топология | InnoDB binlog | Комментарий |
|---|---|---|
Standalone primary + async replicas на GTID | Да | Основной и полностью поддержанный сценарий |
Primary + async replicas на file/offset | Нет | Сначала миграция на GTID |
Semi-sync AFTER_COMMIT + async replicas | Да | Поддерживается |
Semi-sync AFTER_SYNC | Нет | Архитектурно несовместимо |
Galera cluster nodes | Нет | Пока отсутствует поддержка wsrep |
Async replica от Galera node | Да, но косвенно | При условии, что сама Galera-нода остаётся на FILE binlog |
MaxScale read/write split | Нужно тестировать | Протокол репликации прежний, но failover-логика может требовать пересмотра |
MySQL InnoDB Cluster | Не относится | Это отдельный мир, не MariaDB |
Ограничения и открытые проблемы
Фича не зря идёт как опциональная. Перед продакшн-включением ограничения лучше понимать заранее.
Ограничения по дизайну в 12.3.1
При переходе на новый binlog старые
.binфайлы автоматически не мигрируются. In-place преобразования нет.GTID обязателен. Классические file/offset механизмы больше не являются рабочим интерфейсом.
MASTER_POS_WAIT()и похожие file/offset API недоступны. Вместо них нуженMASTER_GTID_WAIT().sync_binlogформально принимается, но фактически игнорируется. Контроль долговечности теперь идёт черезinnodb_flush_log_at_trx_commit.Инструменты, которые читают
.binнапрямую, формат.ibbне поймут.AFTER_SYNCдля semi-sync не поддерживается. Рабочая схема — толькоAFTER_COMMIT.Galera в 12.3.1 с этим режимом не поддерживается.
Открытые вопросы в JIRA
На март 2026 года стоит обратить внимание как минимум на следующее.
MDEV-38462 — возможные проблемы crash recovery при недостаточном innodb_log_file_size в новом режиме. Это логично: теперь redo log несёт не только обычные изменения InnoDB, но и binlog payload. Если лог слишком мал относительно write-rate, recovery может упереться в его размер.
MDEV-38304 — запрос на сохранение binlog в archived redo log. Это не блокер, но показатель того, что история с архивированием ещё развивается.
Сама основная реализация идёт по MDEV-34705. Перед продакшн-включением имеет смысл просмотреть и связанные подзадачи.
Что важно учесть на тестовом стенде и в проде
Есть несколько практических замечаний, которые легко упустить.
Во-первых, не стоит тестировать innodb_flush_log_at_trx_commit=0 на shared cloud storage с агрессивным host caching. Можно получить красивые, но бесполезные цифры. Если хочется увидеть реальное поведение, лучше использовать выделённый Premium SSD или Ultra Disk и отключать лишнее кэширование на стороне платформы.
Во-вторых, из-за предварительного выделения .ibb по 1 ГБ потребление диска выглядит «большим» уже в самом начале. Это не баг, а особенность формата, но для алертов и capacity planning это важно.
В-третьих, sync_binlog, который теперь игнорируется, может стать неприятной ловушкой при миграции. Если команды исторически использовали этот параметр как индикатор «надежного binlog», мониторинг и runbook нужно пересмотреть.
Дизайн бенчмарка
В статье сравнивались две конфигурации:
binlog_storage_engine=FILE— классический вариант;binlog_storage_engine=innodb— новый вариант.
Каждая из них тестировалась в трёх профилях долговечности.
D1: полная долговечность
innodb_flush_log_at_trx_commit=1 sync_binlog=1
Это тот самый production-grade режим, где раньше commit стоил два fsync().
D2: буферизация на стороне ОС
innodb_flush_log_at_trx_commit=2 sync_binlog=0
D3: максимальная пропускная способность
innodb_flush_log_at_trx_commit=0
Отдельно измерялись:
количество TPS через
sysbench oltp_write_only;скорость коммитов, включая перцентили p50/p95/p99;
время коммита для большой транзакции;
время восстановления после
kill -9;отставание (lag) реплики под нагрузкой;
объём записи в журнал redo log;
косвенно количество
fsync()через системные метрики.
Тесты запускались при 1, 8, 32 и 64 потоках.
Лабораторный стенд
Тестовый стенд был развёрнут в Azure.
Primary:
Standard_E4ds_v5;4 vCPU;
32 ГБ RAM.
Replica:
Standard_E2ds_v5;2 vCPU;
16 ГБ RAM.
Диски:
P30 Premium SSD 500 ГБ;
5000 IOPS;
host caching отключён.
ОС — Ubuntu 24.04 LTS. Использовалась MariaDB 12.3.1 RC из binary tarball. Каталог данных, redo log и binlog находились на одном P30-диске в /data/mysql.
Параметры теста:
sysbench oltp_write_only;4 таблицы по 1 млн строк;
60 секунд на прогон;
15 секунд прогрева;
2 повтора на каждую точку;
buffer pool 20 ГБ;
redo log 4 ГБ.
Результаты: D1 даёт 2.4–3.3x прироста TPS
Вот ключевая таблица для полного режима долговечности:
Threads | FILE TPS | InnoDB TPS | Ускорение | FILE p99 | InnoDB p99 |
|---|---|---|---|---|---|
1 | 129 | 307 | 2.4x | 16.1 ms | 8.1 ms |
8 | 444 | 1073 | 2.4x | 37.3 ms | 15.1 ms |
32 | 1392 | 4625 | 3.3x | 43.8 ms | 34.1 ms |
64 | 2564 | 8279 | 3.2x | 72.1 ms | 29.4 ms |
Даже на одном потоке выигрыш уже заметен. Под высокой конкуренцией он становится ещё более выразительным. Это важный момент: эффект не сводится к «косметическому улучшению». Убирается действительно дорогой системный вызов с горячего пути commit.
Но ещё важнее история с p99. В FILE-режиме хвостовая задержка растёт очень резко: с 16 до 72 мс. В режиме InnoDB binlog рост тоже есть, но он существенно мягче: с 8 до 29 мс.
Для прикладных команд это иногда даже важнее, чем рост среднего TPS.
D2 и D3: когда fsync() уже убрали, преимущества почти нет
Результаты для D2/D3 выглядят так:
Threads | FILE D2 | InnoDB D2 | FILE D3 | InnoDB D3 |
|---|---|---|---|---|
1 | 2950 | 2861 | 2943 | 2985 |
8 | 9961 | 9924 | 10027 | 11681 |
32 | 10818 | 11091 | 11202 | 10973 |
64 | 10815 | 10462 | 11121 | 10751 |
При насыщении оба варианта упираются примерно в один и тот же потолок. В данном случае ограничение уже не в синхронизации binlog, а в записи страниц InnoDB и лимите диска по IOPS.
Это, кстати, хороший контрольный результат. Он подтверждает, что основной выигрыш D1 возникает именно из-за устранения дополнительного fsync() binlog. Когда fsync() уже нет, устранять больше нечего.
Восстановление после аварии: скорость сопоставима, архитектура чище
Результаты recovery были такими:
Профиль | Время восстановления | Страниц к восстановлению | Комментарий |
|---|---|---|---|
file_d1 | 34.1 s | 54820 | XA prepared, 2PC reconciliation |
innodb_d1 | 39.2 s | 41688 | Единый recovery path |
file_d2 | 29.1 s | 45430 | Нужна сверка binlog и InnoDB |
innodb_d2 | 27.1 s | 43219 | Единый recovery path |
file_d3 | 33.1 s | 44309 | Согласование состояния после краха |
innodb_d3 | 39.2 s | 44846 | Единый recovery path |
По wall-clock времени здесь нет драматического разрыва. Но архитектурно разница всё равно важна.
В FILE-схеме recovery должен учитывать XA prepare и согласовывать разные источники истины. В режиме InnoDB binlog восстановление становится однопутевым: без отдельной логики для reconciliation между движком и бинарным логом.
То есть дело не только в секунде туда или сюда, а в уменьшении класса потенциальных сбоев.
Большие транзакции: да, раздутие журнала реально существует
Есть и обратная сторона.
В тесте с одной большой транзакцией результаты были такими:
Метрика | FILE | InnoDB |
|---|---|---|
Commit 100K-row UPDATE | 1793 ms | 1564 ms |
Размер журнала | 1.03x | 1.98x |
Изменённые данные | 104 MB | 104 MB |
Записано в журнал | 107 MB | 206 MB |
Именно потому, что binlog теперь проходит через журнал (redo log), общий объём записи в журнал почти удваивается.
Для типичных OLTP-нагрузок с короткими транзакциями это обычно не критично: выигрыш от устранения fsync() перекрывает этот эффект. Но для bulk ETL, массовых INSERT и больших UPDATE это уже надо учитывать отдельно.
Отсюда прямое практическое следствие: innodb_log_file_size нужно подбирать более щедро, чем раньше.
Репликационный lag: узкое место остаётся на стороне реплики
В статье отдельно измеряли отставание (lag) одной async-реплики под sustained 16-thread write load.
Метрика | FILE D1 | InnoDB D1 |
|---|---|---|
TPS primary | 1276 | 1994 |
Lag на 60s | 58s | 89s |
Lag на 120s | 121s | 148s |
Lag на 180s | 185s | 206s |
Lag на 300s | 313s | 321s |
Рост lag | ~1.0 s/s | ~1.1 s/s |
На первый взгляд может показаться, что InnoDB binlog увеличивает задержку, потому что цифры выше. Но правильная интерпретация здесь другая: primary начинает коммитить существенно быстрее, а узким местом становится выполнение SQL apply на стороне реплики.
То есть новая схема не ломает репликацию как таковую. Она просто снимает ограничение с primary, после чего проблема смещается в сторону реплики.
Если дальше включать параллельное исполнение (parallel apply) на реплике, преимущество, вероятно, станет ещё более заметным.
Что это означает для продакшна
Если коротко, то для GTID-based async replication это одно из самых сильных улучшений MariaDB 12.3.
Что мы получаем:
2.4–3.3x прироста TPS в строгом durability-профиле;
заметно более низкий p99 при росте конкуренции;
устранение рассинхронизации между binlog и InnoDB как отдельного класса проблем;
более простой backup/restore и ресинк реплик;
меньше ручной арифметики с file/offset;
более чистую и предсказуемую модель восстановления.
Но есть и условия:
нужен GTID;
нужно забыть про старую file/offset-операционку;
придётся внимательнее относиться к
innodb_log_file_size;нельзя рассчитывать на Galera в текущем релизе;
часть старых binlog-readers придётся заменить или проверить на совместимость.
Когда я бы включал эту фичу
В первую очередь — на схеме с одним мастером и асинхронными репликами, где используется InnoDB и уже принят GTID как стандартная модель репликации.
Особенно хорошо это выглядит там, где:
важна низкая задержка;
тяжёлая OLTP-нагрузка упирается в производительность диска;
инфраструктура живёт в облаке и каждый лишний
fsync()реально стоит дорого;хочется упростить backup/restore и полное перевосстановление реплик.
Когда я бы не спешил
Есть несколько сценариев, где я бы был осторожнее.
Galera-кластеры: пока просто нет поддержки.
ETL-нагрузки с очень большими batch-транзакциями: сначала я бы проверил, достаточно ли места для хранения журнала и корректно ли настроен его размер.
Инсталляции, завязанные на старые инструменты, которые читают
.binнапрямую.Вы ещё не готовы перейти на GTID как на единственный интерфейс репликации.
Рекомендации по внедрению
Если планировать пилот, я бы шёл так:
Сначала убедиться, что вся репликация уже живёт на GTID.
Отдельно проверить инструменты: backup, CDC, мониторинг, failover automation.
Заранее увеличить
innodb_log_file_sizeминимум до 4 ГБ, а при интенсивной записи рассматривать и более высокие значения.Прогнать собственный write-heavy benchmark именно на вашей storage-платформе.
Добавить мониторинг checkpoint age и признаков pressure на redo log.
Проверить, что никто внутри команды не рассчитывает на
sync_binlogкак на реально работающий рычаг.
Итог
Для OLTP-сценариев на MariaDB 12.3 это, на мой взгляд, одна из самых важных новых возможностей за последние годы.
Исторически binlog был компромиссом: без него нельзя, но за него приходилось платить и по TPS, и по задержкам, и по сложности эксплуатации. В режиме binlog_storage_engine=innodb часть этой цены просто исчезает, потому что исчезает сама архитектурная причина двойной синхронизации.
Особенно ценно, что выигрыш здесь не только в бенчмарках. Меняется и повседневная операционная модель: backup становится чище, resync реплик — проще, split-brain recovery — понятнее, а file/offset-арифметика уходит в прошлое.
Если у вас уже есть GTID, InnoDB и обычная асинхронная репликация, эту возможность точно стоит как минимум протестировать.
Глоссарий
GTID (Global Transaction ID)
Глобальный идентификатор транзакции — уникальный номер, присваиваемый каждой транзакции в репликационной топологии MariaDB/MySQL. Используется для отслеживания положения репликации независимо от файлов и байтовых смещений. Формат: domain_id-server_id-sequence_number.
WAL (Write-Ahead Logging)
Протокол гарантии долговечности, при котором все изменения сначала записываются в журнал (redo log) перед применением к основным данным. Это гарантирует, что даже при сбое питания или краше базы данные можно восстановить из журнала.
Durability (долговечность)
В контексте СУБД это свойство ACID, гарантирующее, что подтверждённые транзакции сохраняются даже после сбоя.
Redo log / Redo Journal
Журнал повторного выполнения в InnoDB, который содержит запись всех изменений до их применения к данным. Используется при восстановлении после краша для воспроизведения транзакций, которые были записаны, но не полностью применены.
Binlog (Binary Log)
Бинарный журнал изменений базы данных в MariaDB/MySQL. Содержит события репликации (INSERT, UPDATE, DELETE и т.д.) и используется для репликации и восстановления данных.
Fsync
Системный вызов, который принудительно синхронизирует все буферизованные данные процесса с диском. В контексте БД это критичная операция для гарантии долговечности, но затратная по производительности.
2PC (Two-Phase Commit)
Двухфазный протокол коммита — архитектурный подход, при котором есть две независимые точки коммита, которые необходимо согласовывать между собой. В старой модели MariaDB это были отдельные коммиты для binlog и InnoDB.
XA (eXtended Architecture)
Распределённый протокол транзакций для управления транзакциями, затрагивающими несколько источников данных. В контексте MariaDB используется для согласования состояния между InnoDB и binlog при двухфазном коммите.
Replication Lag (Репликационная задержка)
Временная разница между моментом коммита транзакции на primary и моментом её применения на replica. Измеряется в секундах или миллисекундах.
Split-brain (Расщепление мозга)
Сценарий отказа репликации, при котором primary и replica теряют связь и независимо принимают данные, что приводит к несогласованности состояния на обоих серверах.
Group Commit
Оптимизация, при которой несколько транзакций синхронизируются и записываются на диск вместе, амортизируя стоимость fsync() между несколькими операциями.
Crash Recovery
Процесс восстановления базы данных после неожиданного завершения сервера (падение, отключение питания и т.д.) путём воспроизведения операций из redo log и отката неполных транзакций из undo log.
Semi-sync Replication
Режим репликации, при котором primary ждёт подтверждения от replica перед завершением коммита. AFTER_SYNC означает ожидание после синхронизации replica, AFTER_COMMIT — после локального коммита.
Galera Cluster
Синхронный кластер репликации для MariaDB/MySQL, в котором все узлы содержат идентичные копии данных благодаря протоколу write sets и сертификации конфликтов.
IOPS (Input/Output Operations Per Second)
Количество операций чтения/записи, которые диск может выполнить в секунду. Один из ключевых параметров производительности хранилища для операций БД.
Throughput (Пропускная способность)
Количество успешно обработанных операций в единицу времени (обычно TPS — транзакции в секунду или QPS — запросы в секунду).
Tail Latency (p99, p95 latency)
Задержка наихудшего случая для 99-го, 95-го процентиля операций. Показывает, какой процент операций превышает эту задержку, и важна для пользовательского опыта.
TPS (Transactions Per Second)
Количество транзакций, успешно обработанных сервером в секунду. Основная метрика производительности для OLTP-систем.
Ссылки
Оригинальная статья: https://www.dbi-services.com/blog/mariadb-12-3-binlog-inside-innodb/
MDEV-34705: https://jira.mariadb.org/browse/MDEV-34705
MDEV-38462: https://jira.mariadb.org/browse/MDEV-38462
MariaDB 12.3 Changes & Improvements: https://mariadb.com/docs/release-notes/community-server/12.3/mariadb-12.3-changes-and-improvements
Official binlog implementation documentation: https://github.com/MariaDB/server/blob/knielsen_binlog_in_engine/Docs/replication/binlog.md
MariaDB Foundation release posts: https://mariadb.org/tag/mariadb-releases/
