Skip to content
Learning Platform
Intermediate
30 minutes
mysql binlog replication cdc

Prerequisites:

  • 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.

PostgreSQL: WAL -> Logical Decoding
WALФизический лог
wal_level=logical
Logical Decoding
pgoutput plugin
CDC Events
Изначально физический формат
Требует декодирования
Оптимизирован для durability
MySQL: Binary LogRecommended
Binary LogЛогический лог
binlog_format=ROW
CDC Events
Изначально логический формат
Готов для репликации
Оптимизирован для replication
ХарактеристикаPostgreSQL WALMySQL Binary Log
Изначальное назначениеDurability (crash recovery)Replication
Формат по умолчаниюФизический (блоки страниц)Логический (SQL или строки)
Для CDC требуетсяLogical decoding (pgoutput)Только binlog_format=ROW
Overhead для CDCДополнительная информация в WALУже включена
Managed servicesТребует wal_level=logicalBinlog включен по умолчанию

Ключевое отличие: 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 могут генерировать огромные события
Приложение
UPDATE products
SET price = price * 1.1
WHERE category = 'electronics'
ROW форматRecommended
UPDATE_ROWS_EVENT
Row 1: before={id:1, price:100} after={id:1, price:110}
Row 2: before={id:2, price:200} after={id:2, price:220}
Row N: ...
Детерминированный
Полная информация о значениях
Единственный формат для 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 записывает SQL-запросы как есть, и недетерминированные функции (NOW(), RAND(), UUID()) дают разные результаты на разных серверах.

Сравнительная таблица форматов

ХарактеристикаROWSTATEMENTMIXED
ДетерминизмДетерминированныйНедетерминированныйПеременный
Размер binlogБольшойМаленькийПеременный
CDC совместимость✅ Да❌ Нет⚠️ Не рекомендуется
Debezium поддержкаПолнаяНетОграниченная
РекомендацияИспользуйте для CDCТолько legacyИзбегайте

Критическое требование для Debezium: Установите binlog_format=ROW. Без этого Debezium MySQL connector не сможет корректно работать.

Binlog Event Types: Структура событий

Каждое изменение в MySQL записывается как последовательность binlog events. Понимание типов событий необходимо для отладки и мониторинга CDC.

Основные типы событий

App
MySQL
Binlog
Debezium
BEGIN TRANSACTIONGTID_EVENT (transaction ID)CREATE TABLE products (...)QUERY_EVENT (DDL statement)INSERT INTO customers VALUES (1, Alice)TABLE_MAP_EVENT (schema)WRITE_ROWS_EVENT (row data)UPDATE customers SET name=BobTABLE_MAP_EVENTUPDATE_ROWS_EVENT (before + after)DELETE FROM customers WHERE id=1DELETE_ROWS_EVENT (deleted row)COMMITXID_EVENT (transaction commit)Read eventsStream of events

Описание ключевых событий

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-значный номер, инкрементируется при каждой ротации

Когда происходит ротация?

  1. Достигнут max_binlog_size (по умолчанию 1GB)

    SHOW VARIABLES LIKE 'max_binlog_size';
    -- 1073741824 (1GB)
  2. Перезапуск MySQL сервера

    • При старте всегда создаётся новый файл
    • Гарантирует чистое начало
  3. Команда FLUSH LOGS

    FLUSH BINARY LOGS;
    -- Принудительная ротация
  4. Команда 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 продолжает с сохранённой позиции
  • Реплики используют позицию для синхронизации с мастером
Binlog файлы
mysql-bin.000001
0-1073741824 bytes
Status: CLOSED
mysql-bin.000002
0-1073741824 bytes
Status: CLOSED
mysql-bin.000003
0-524288 bytes
Status: ACTIVE
Debezium Connector
Current Position:
mysql-bin.000003
Offset: 154
Reading
Ротация происходит при:
max_binlog_size достигнут | Перезапуск MySQL | FLUSH BINARY LOGS
Проверка знаний
Из каких двух частей состоит позиция записи в MySQL binlog, и для чего Debezium её использует?
Ответ
Позиция состоит из имени файла (например, mysql-bin.000003) и смещения в байтах (offset). 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 DecodingMySQL Binary Log
Формат по умолчаниюФизический (блоки страниц)Логический (ROW/STATEMENT)
Включение CDCwal_level=logicalbinlog_format=ROW
Плагин декодированияpgoutput (или wal2json)Не требуется
Replication SlotОбязателен для CDCНе используется (position tracking)
PublicationsФильтрация таблицbinlog-do-db / binlog-ignore-db
REPLICA IDENTITYDEFAULT / 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.

Ключевые выводы

  1. MySQL binlog изначально логический — предназначен для репликации, не требует декодирования как PostgreSQL WAL
  2. ROW формат обязателен для CDC — STATEMENT недетерминированный, MIXED непредсказуемый
  3. Binlog события структурированы — TABLE_MAP + WRITE/UPDATE/DELETE + XID образуют транзакцию
  4. Ротация binlog автоматическая — новый файл при достижении max_binlog_size или перезапуске
  5. Position tracking — Debezium отслеживает (binlog_file, offset) для fault tolerance
  6. Автоочистка критична — установите binlog_expire_logs_seconds больше максимального downtime Debezium

Что дальше?

Мы разобрались с binlog форматами и структурой событий. Но для production CDC недостаточно знать позицию в файле — нужна глобальная идентификация транзакций.

В следующем уроке мы изучим GTID (Global Transaction ID) — систему уникальных идентификаторов транзакций, которая делает MySQL репликацию и CDC fault-tolerant и масштабируемой. Вы узнаете, почему GTID критичен для AWS Aurora MySQL и как настроить GTID mode для Debezium.

Check Your Understanding

Score: 0 of 0
Conceptual
Question 1 of 4. Почему для CDC с Debezium требуется именно формат binlog_format=ROW, а не STATEMENT или MIXED?

Finished the lesson?

Mark it as complete to track your progress