Требуемые знания:
- module-2/01-logical-decoding-deep-dive
MySQL Binary Log: Архитектура и форматы
В Модуле 2 мы изучали PostgreSQL WAL и logical decoding — механизм преобразования физического лога транзакций в логические события. MySQL использует другой подход: binary log (binlog) изначально создавался для репликации и уже содержит логические изменения данных.
В этом уроке мы глубоко погрузимся в архитектуру MySQL binlog, разберём три формата записи (ROW, STATEMENT, MIXED), поймём структуру событий и научимся работать с ротацией логов. Это фундамент для понимания Debezium MySQL connector.
Binary Log vs Write-Ahead Log
PostgreSQL и MySQL решают задачу репликации по-разному. Понимание этих различий критически важно для настройки CDC.
Требует декодирования
Оптимизирован для durability
Готов для репликации
Оптимизирован для replication
| Характеристика | PostgreSQL WAL | MySQL Binary Log |
|---|---|---|
| Изначальное назначение | Durability (crash recovery) | Replication |
| Формат по умолчанию | Физический (блоки страниц) | Логический (SQL или строки) |
| Для CDC требуется | Logical decoding (pgoutput) | Только binlog_format=ROW |
| Overhead для CDC | Дополнительная информация в WAL | Уже включена |
| Managed services | Требует wal_level=logical | Binlog включен по умолчанию |
Ключевое отличие: PostgreSQL WAL изначально физический и требует преобразования через logical decoding. MySQL binlog изначально логический и готов для CDC при правильном формате.
Binlog Formats: ROW, STATEMENT, MIXED
MySQL поддерживает три формата записи в binlog. Выбор формата критически влияет на возможность использования CDC.
ROW формат (Required for CDC)
ROW формат записывает фактические изменения строк — до и после модификации. Это единственный формат, подходящий для CDC.
-- При INSERT в таблицу customers
INSERT INTO customers (id, name, email) VALUES (1, 'Alice', '[email protected]');
-- Binlog ROW формат записывает:
-- WRITE_ROWS_EVENT:
-- Table: customers
-- Row: {id: 1, name: 'Alice', email: '[email protected]'}
Преимущества:
- Детерминированность — одинаковый результат на реплике
- Полная информация о значениях строк
- Работает с недетерминированными функциями (NOW(), RAND(), UUID())
- Единственный формат, совместимый с CDC
Недостатки:
- Больший размер binlog
- Массовые UPDATE могут генерировать огромные события
SET price = price * 1.1
WHERE category = 'electronics'
Row 1: before={id:1, price:100} after={id:1, price:110}
Полная информация о значениях
Единственный формат для CDC
STATEMENT формат (Legacy, NOT for CDC)
STATEMENT формат записывает SQL запросы, как есть.
-- При INSERT в таблицу orders
INSERT INTO orders (id, customer_id, created_at)
VALUES (1, 42, NOW());
-- Binlog STATEMENT формат записывает:
-- QUERY_EVENT:
-- Query: INSERT INTO orders (id, customer_id, created_at) VALUES (1, 42, NOW());
Проблема для CDC:
-- На мастере NOW() вернул: 2026-02-01 10:30:00
-- На реплике NOW() вернёт: 2026-02-01 10:30:05
-- Результат: РАЗНЫЕ данные на мастере и реплике!
Недетерминированные функции, ломающие STATEMENT:
NOW(),CURDATE(),CURRENT_TIMESTAMP()— время выполненияRAND()— случайные числаUUID()— уникальные идентификаторыLAST_INSERT_ID()— зависит от контекста
Когда был полезен:
- Старые версии MySQL (до 5.1)
- Экономия места на binlog
- Репликация только между идентичными MySQL серверами
Вердикт: STATEMENT формат НЕ подходит для CDC. Не используйте.
MIXED формат (Unpredictable, Avoid)
MIXED формат — MySQL автоматически выбирает ROW или STATEMENT в зависимости от запроса.
-- Детерминированный запрос → STATEMENT
INSERT INTO customers (id, name) VALUES (1, 'Bob');
-- Недетерминированный запрос → ROW
INSERT INTO orders (id, created_at) VALUES (1, NOW());
Проблема для CDC:
- Непредсказуемость — вы не контролируете формат
- Разные события имеют разную структуру
- Debezium должен обрабатывать оба формата
- Усложняет отладку и мониторинг
Вердикт: MIXED формат избегайте. Используйте только ROW для CDC.
Проверка знанийПочему формат ROW является единственным подходящим для CDC, а STATEMENT — нет?
Сравнительная таблица форматов
| Характеристика | ROW | STATEMENT | MIXED |
|---|---|---|---|
| Детерминизм | Детерминированный | Недетерминированный | Переменный |
| Размер binlog | Большой | Маленький | Переменный |
| CDC совместимость | ✅ Да | ❌ Нет | ⚠️ Не рекомендуется |
| Debezium поддержка | Полная | Нет | Ограниченная |
| Рекомендация | Используйте для CDC | Только legacy | Избегайте |
Критическое требование для Debezium: Установите
binlog_format=ROW. Без этого Debezium MySQL connector не сможет корректно работать.
Binlog Event Types: Структура событий
Каждое изменение в MySQL записывается как последовательность binlog events. Понимание типов событий необходимо для отладки и мониторинга CDC.
Основные типы событий
Описание ключевых событий
GTID_EVENT (Global Transaction ID Event)
- Уникальный идентификатор транзакции в формате
server_uuid:transaction_id - Используется только при
gtid_mode=ON - Позволяет точное отслеживание позиции репликации
- Debezium использует GTID для fault tolerance
QUERY_EVENT
- Записывает DDL операции: CREATE, ALTER, DROP
- Записывает BEGIN (начало транзакции)
- Содержит полный SQL текст запроса
TABLE_MAP_EVENT
- Связывает table ID с именем таблицы и схемой
- Предшествует каждому WRITE/UPDATE/DELETE событию
- Содержит метаданные: имена колонок, типы данных
- Критичен для Debezium: без него невозможно декодировать row events
WRITE_ROWS_EVENT (INSERT)
TABLE_MAP_EVENT: customers (id INT, name VARCHAR, email VARCHAR)
WRITE_ROWS_EVENT:
Row 1: {id: 1, name: 'Alice', email: '[email protected]'}
Row 2: {id: 2, name: 'Bob', email: '[email protected]'}
UPDATE_ROWS_EVENT
TABLE_MAP_EVENT: customers
UPDATE_ROWS_EVENT:
Row: before={id: 1, name: 'Alice'}, after={id: 1, name: 'Alice Updated'}
DELETE_ROWS_EVENT
TABLE_MAP_EVENT: customers
DELETE_ROWS_EVENT:
Row: {id: 1, name: 'Alice', email: '[email protected]'}
XID_EVENT (Transaction Commit)
- Фиксирует успешное завершение транзакции
- Содержит XID (transaction ID) для InnoDB
- Debezium использует для группировки событий одной транзакции
Пример последовательности событий
-- SQL транзакция
BEGIN;
INSERT INTO customers (id, name) VALUES (1, 'Alice');
UPDATE customers SET name='Alice Updated' WHERE id=1;
COMMIT;
Binlog последовательность:
1. GTID_EVENT: server_uuid:1
2. QUERY_EVENT: BEGIN
3. TABLE_MAP_EVENT: customers (id, name)
4. WRITE_ROWS_EVENT: {id: 1, name: 'Alice'}
5. TABLE_MAP_EVENT: customers (id, name)
6. UPDATE_ROWS_EVENT: before={id:1, name:'Alice'}, after={id:1, name:'Alice Updated'}
7. XID_EVENT: Transaction commit
Binlog Rotation: Управление файлами логов
MySQL не хранит весь binlog в одном файле. Вместо этого используется ротация — создание новых файлов по достижении определённых условий.
Именование файлов binlog
mysql-bin.000001
mysql-bin.000002
mysql-bin.000003
...
mysql-bin.999999
Формат: {basename}.{sequence_number}
basename— настраивается черезlog-binпараметр (по умолчаниюmysql-bin)sequence_number— 6-значный номер, инкрементируется при каждой ротации
Когда происходит ротация?
-
Достигнут max_binlog_size (по умолчанию 1GB)
SHOW VARIABLES LIKE 'max_binlog_size'; -- 1073741824 (1GB) -
Перезапуск MySQL сервера
- При старте всегда создаётся новый файл
- Гарантирует чистое начало
-
Команда FLUSH LOGS
FLUSH BINARY LOGS; -- Принудительная ротация -
Команда RESET MASTER (опасно!)
RESET MASTER; -- Удаляет ВСЕ binlog файлы и начинает с .000001 -- Используйте ТОЛЬКО в dev/test окружении!
Индексный файл mysql-bin.index
MySQL поддерживает список всех активных binlog файлов в индексном файле.
# Содержимое mysql-bin.index
/var/lib/mysql/mysql-bin.000001
/var/lib/mysql/mysql-bin.000002
/var/lib/mysql/mysql-bin.000003
Назначение:
- Быстрый поиск всех binlog файлов
- Используется при репликации для навигации между файлами
- Debezium читает индекс для отслеживания доступных логов
Binlog Position Tracking
Каждая запись в binlog имеет точную позицию, состоящую из двух частей:
Position = (binlog_file, binlog_offset)
Пример:
(mysql-bin.000003, 154)
↑ ↑
filename offset (байты)
Для чего нужна позиция:
- Debezium отслеживает текущую позицию чтения
- При перезапуске Debezium продолжает с сохранённой позиции
- Реплики используют позицию для синхронизации с мастером
0-1073741824 bytes
Status: CLOSED
0-1073741824 bytes
Status: CLOSED
0-524288 bytes
Status: ACTIVE
mysql-bin.000003
Offset: 154
max_binlog_size достигнут | Перезапуск MySQL | FLUSH BINARY LOGS
Проверка знанийИз каких двух частей состоит позиция записи в MySQL binlog, и для чего Debezium её использует?
Автоматическая очистка старых binlog
Старые binlog файлы занимают место на диске. MySQL автоматически удаляет файлы старше определённого времени.
-- Проверить настройку auto-purge
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';
-- 2592000 (30 дней по умолчанию)
-- Изменить retention период (7 дней)
SET GLOBAL binlog_expire_logs_seconds = 604800;
Важно для Debezium:
- Если Debezium остановлен дольше, чем
binlog_expire_logs_seconds, старые binlog будут удалены - При возобновлении Debezium не сможет прочитать пропущенные изменения
- Решение: установите достаточно большой retention период или используйте binlog backups
Предупреждение: Установка слишком большого
binlog_expire_logs_secondsможет заполнить диск. Мониторьте используемое пространство.
Verification Commands: Проверка binlog настроек
Проверить binlog format
SHOW VARIABLES LIKE 'binlog_format';
Ожидаемый результат:
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW |
+---------------+-------+
Если видите STATEMENT или MIXED — измените:
-- Глобальное изменение (требует SUPER привилегии)
SET GLOBAL binlog_format = 'ROW';
-- Для текущей сессии
SET SESSION binlog_format = 'ROW';
Для постоянного изменения добавьте в my.cnf:
[mysqld]
binlog_format = ROW
Проверить список binlog файлов
SHOW BINARY LOGS;
Пример вывода:
+------------------+-----------+-----------+
| Log_name | File_size | Encrypted |
+------------------+-----------+-----------+
| mysql-bin.000001 | 177 | No |
| mysql-bin.000002 | 1074 | No |
| mysql-bin.000003 | 524 | No |
+------------------+-----------+-----------+
Проверить текущую позицию мастера
SHOW MASTER STATUS;
Пример вывода:
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 | 524 | | | |
+------------------+----------+--------------+------------------+-------------------+
Интерпретация:
File— текущий активный binlog файлPosition— текущий offset в байтахExecuted_Gtid_Set— для GTID mode (следующий урок)
Просмотреть события в binlog
-- Показать первые 20 событий
SHOW BINLOG EVENTS IN 'mysql-bin.000003' LIMIT 20;
Пример вывода:
+------------------+------+----------------+-----------+-------------+---------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+------+----------------+-----------+-------------+---------------------------------------+
| mysql-bin.000003 | 4 | Format_desc | 1 | 126 | Server ver: 8.0.40, Binlog ver: 4 |
| mysql-bin.000003 | 126 | Previous_gtids | 1 | 157 | |
| mysql-bin.000003 | 157 | Anonymous_Gtid | 1 | 236 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| mysql-bin.000003 | 236 | Query | 1 | 322 | BEGIN |
| mysql-bin.000003 | 322 | Table_map | 1 | 385 | table_id: 108 (test.customers) |
| mysql-bin.000003 | 385 | Write_rows | 1 | 441 | table_id: 108 flags: STMT_END_F |
| mysql-bin.000003 | 441 | Xid | 1 | 472 | COMMIT /* xid=47 */ |
+------------------+------+----------------+-----------+-------------+---------------------------------------+
Ключевые колонки:
Pos— позиция начала событияEvent_type— тип события (Table_map, Write_rows, Xid)End_log_pos— позиция конца событияInfo— дополнительная информация
Проверить binlog retention
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';
Рекомендация: Для CDC окружения с Debezium установите минимум 7 дней (604800 секунд):
SET GLOBAL binlog_expire_logs_seconds = 604800;
Сравнение с PostgreSQL WAL
Для тех, кто прошёл Модуль 2, вот прямое сравнение подходов PostgreSQL и MySQL.
| Аспект | PostgreSQL WAL + Logical Decoding | MySQL Binary Log |
|---|---|---|
| Формат по умолчанию | Физический (блоки страниц) | Логический (ROW/STATEMENT) |
| Включение CDC | wal_level=logical | binlog_format=ROW |
| Плагин декодирования | pgoutput (или wal2json) | Не требуется |
| Replication Slot | Обязателен для CDC | Не используется (position tracking) |
| Publications | Фильтрация таблиц | binlog-do-db / binlog-ignore-db |
| REPLICA IDENTITY | DEFAULT / FULL / INDEX | Всегда полные данные в ROW |
| Позиция репликации | LSN (Log Sequence Number) | (binlog_file, offset) |
| GTID поддержка | Нет | Да (опционально) |
| Автоочистка | По LSN replication slot | По времени (expire_logs_seconds) |
Ключевой вывод: MySQL binlog проще для CDC, потому что уже содержит логические изменения. PostgreSQL требует дополнительный слой (logical decoding), но даёт более гибкий контроль через publications и REPLICA IDENTITY.
Ключевые выводы
- MySQL binlog изначально логический — предназначен для репликации, не требует декодирования как PostgreSQL WAL
- ROW формат обязателен для CDC — STATEMENT недетерминированный, MIXED непредсказуемый
- Binlog события структурированы — TABLE_MAP + WRITE/UPDATE/DELETE + XID образуют транзакцию
- Ротация binlog автоматическая — новый файл при достижении max_binlog_size или перезапуске
- Position tracking — Debezium отслеживает (binlog_file, offset) для fault tolerance
- Автоочистка критична — установите
binlog_expire_logs_secondsбольше максимального downtime Debezium
Что дальше?
Мы разобрались с binlog форматами и структурой событий. Но для production CDC недостаточно знать позицию в файле — нужна глобальная идентификация транзакций.
В следующем уроке мы изучим GTID (Global Transaction ID) — систему уникальных идентификаторов транзакций, которая делает MySQL репликацию и CDC fault-tolerant и масштабируемой. Вы узнаете, почему GTID критичен для AWS Aurora MySQL и как настроить GTID mode для Debezium.
Проверьте понимание
Закончили урок?
Отметьте его как пройденный, чтобы отслеживать свой прогресс