Что такое replication lag и почему на VDS он всплывает чаще
Replication lag в MySQL — это ситуация, когда реплика применяет изменения медленнее, чем источник их генерирует. Итог знакомый: чтения с реплики становятся «устаревшими», отчёты показывают вчерашние данные, а при переключении ролей вы рискуете получить долгий «догон» или потерять хвост транзакций (если переключение сделано без учёта фактического прогресса).
На VDS лаг проявляется чаще, чем на «железе», по двум причинам: дисковая подсистема (IOPS/latency) менее предсказуема и чувствительна к пикам fsync, а CPU и планировщик могут давать больше вариативности под смешанной нагрузкой. Репликация любит стабильное хранилище и понятный профиль записи.
Для диагностики полезно сразу разделить проблему на две разные истории:
- I/O-thread не успевает читать события из binlog источника (узкое место: сеть, источник, ограничения канала, настройки semi-sync).
- SQL-thread (apply) не успевает применять то, что уже скачано в relay log (узкое место: диск на реплике, блокировки, параллелизм, большие транзакции, flush-профиль InnoDB).
Почему Seconds_Behind_Master часто вводит в заблуждение
Почти все начинают с Seconds_Behind_Master из SHOW REPLICA STATUS (или SHOW SLAVE STATUS в старых версиях). Это полезный индикатор, но далеко не «истинное время отставания».
Типичные ловушки:
NULL— не «0». Обычно это означает, что репликация не работает как ожидалось: поток остановлен, есть ошибка или метрика не вычисляется.- «0 при проблемах». Если источник временно не генерирует события, метрика может стать 0, даже если у вас есть зависшие/долго применяющиеся транзакции.
- Часы и NTP. Расчёт зависит от времени события в binlog и локального времени реплики; при скачках времени возможны странные значения.
В продакшене полезно иметь внешний SLI лага (например, heartbeat-таблица). Ниже тоже обсудим, как это помогает увидеть «микролаги» и реальные хвосты.
Быстрая проверка статуса репликации
Снимите статус одной командой и смотрите не только Seconds_Behind_Master, а состояние потоков и ошибки.
mysql -e "SHOW REPLICA STATUS\G"
Поля, которые обычно дают ответ «где затык» за 1 минуту:
Replica_IO_RunningиReplica_SQL_Running(илиSlave_IO_Running/Slave_SQL_Running)Last_IO_Error,Last_SQL_ErrorMaster_Log_File,Read_Master_Log_Pos(дочитали ли binlog)Relay_Log_File,Relay_Log_Pos,Relay_Log_Space(насколько вырос relay log)Retrieved_Gtid_Set,Executed_Gtid_Set(если включён GTID)

GTID: как понять, где именно «застряли»
GTID (Global Transaction ID) делает репликацию управляемее: проще сравнивать прогресс, пересоздавать реплики и выполнять безопасные переключения. Для диагностики лага GTID особенно удобен тем, что показывает разницу между «скачано» и «применено».
Ключевая логика:
- Если
Retrieved_Gtid_Setрастёт, аExecuted_Gtid_Setзаметно отстаёт — I/O-thread успевает, а apply (SQL-thread/worker’ы) не вывозит. - Если оба растут медленно — проблема может быть вне реплики: сеть, лимиты канала, источник не отдаёт binlog достаточно быстро, особенности semi-sync.
Если ваша операционная процедура включает planned failover, полезно держать отдельный короткий ранбук по переключениям и GTID. У нас есть связанная заметка про практику переключений: failover с GTID и semi-sync в MySQL.
Практичная метрика «очереди применений»
В терминах реплики есть две скорости: «получать события» и «применять события». Когда скачивание обгоняет применение, растёт «очередь» в relay log, чаще всего это видно по Relay_Log_Space и по разнице GTID-наборов. Даже без углубления в performance_schema этого обычно достаточно, чтобы выбрать направление: тюнинг диска/flush/параллелизма или поиск сетевого/источникового ограничения.
Relay log: почему «диск решает»
Relay log — это локальные файлы на реплике, куда I/O-thread записывает события, прочитанные из binlog источника. Затем apply-поток(и) их выполняют. На VDS именно этот слой часто первым упирается в диск: запись relay log плюс запись InnoDB (redo/двойная запись, фоновые flush’и) создают пилообразную нагрузку и чувствительность к latency.
Типичный сценарий: сеть и источник «быстрые», I/O-thread активно пишет relay log, очередь растёт, а применение тормозит из-за высокой latency диска или из-за блокировок при применении изменений. В статусе это часто выглядит как рост Relay_Log_Space и увеличение Seconds_Behind_Master.
Проверьте место и I/O-профиль на реплике
Минимальный набор команд, который помогает подтвердить «дисковую» природу лага:
df -h
ls -lh /var/lib/mysql
iostat -x 1
pidstat -d 1
Что обычно настораживает: высокий await и util в iostat, рост очереди, резкие пики записи, а также ситуации, когда диск почти постоянно занят даже при умеренном QPS на реплике.
Parallel replication: как догонять источник, а не ждать часами
Если apply упирается в последовательность выполнения транзакций, самый «дешёвый» по времени эффект даёт parallel replication: несколько worker-потоков применяют независимые транзакции параллельно. На практике это часто превращает «час догоняем» в «минуту догоняем» — но только если транзакции действительно можно распараллелить.
Важно помнить ограничения:
- Если все изменения идут в одну «горячую» таблицу/строку (hot rows), параллелизм упрётся в блокировки.
- Если выставить слишком много worker’ов, можно получить конкуренцию за CPU и рост переключений контекста, а не ускорение.
Базовая настройка parallel replication (MySQL 8)
Стартовая конфигурация для реплики с несколькими vCPU (значения подбирайте по наблюдениям):
mysql -e "SET PERSIST replica_parallel_workers=8;"
mysql -e "SET PERSIST replica_parallel_type='LOGICAL_CLOCK';"
Если у вас старые имена переменных, используйте аналоги slave_parallel_workers и slave_parallel_type.
После включения проверьте, что worker’ы реально заняты. Если они простаивают — значит распараллеливать нечего (высокая конфликтность) или bottleneck в другом месте (I/O, flush, CPU на декодирование, индексы).
binlog и формат: почему ROW часто спасает (и чем платите)
Формат бинарного лога влияет и на объём данных, и на предсказуемость применения. Row Based Replication (RBR) передаёт изменения на уровне строк. Это снижает риск расхождений из-за недетерминированных выражений и обычно лучше сочетается с параллельным применением.
Обратная сторона — больше объём binlog/relay log, больше диска и трафика. На VDS это важно: «толстые» логи могут стать новой причиной лага, особенно если диск и так близок к пределам по IOPS.
Что проверить в binlog-профиле при лаге
- Хватает ли места под binlog/relay log и корректна ли политика очистки.
- Нет ли гигантских транзакций (миллионы строк в одном commit) — они применяются долго и плохо параллелятся.
- Соответствует ли формат (STATEMENT/MIXED/ROW) вашему профилю нагрузки и требованиям к детерминизму.
Если вы часто разбираете инциденты репликации, пригодится отдельный материал про восстановление/точку во времени и работу с binlog: PITR и binlog/GTID в MySQL/MariaDB.
Semi-sync: когда помогает, а когда добавляет задержки
Semi-synchronous replication заставляет источник дождаться подтверждения от реплики, что транзакция получена (как минимум дошла до реплики и записана), прежде чем подтверждать commit клиенту. Это уменьшает риск потери последних транзакций при аварии, но увеличивает latency записи и может «сцепить» судьбу мастера и реплики при деградации реплики.
Если semi-sync включён, при проблемах диска/CPU на реплике вы увидите не только lag, но и просадку записи на источнике. В этом режиме критично иметь мониторинг времени подтверждения и адекватные таймауты/политику fallback на async, чтобы мастер не «встал» вместе с репликой.
innodb_flush_log_at_trx_commit и fsync: цена надёжности
Параметр innodb_flush_log_at_trx_commit определяет, как часто InnoDB делает flush redo log на диск. Значение 1 даёт максимальную надёжность (fsync на каждый commit), но на дисках с высокой latency или при лимитах IOPS это легко становится главным ограничителем и на источнике, и на реплике.
Иногда рассматривают 2 как компромисс (меньше fsync, но риск потери последних секунд при падении ОС). Это решение должно быть осознанным и согласованным с требованиями к целостности данных.
Если лаг приходит «волнами» и совпадает с пиками fsync, начните с измерений (iostat, задержки commit, очереди I/O), а уже потом меняйте
innodb_flush_log_at_trx_commit. Иначе легко обменять один класс проблем на другой.

Heartbeat-замер: как измерять лаг «по-настоящему»
Самый полезный подход в эксплуатации — иметь отдельную метрику лага, не зависящую от особенностей вычисления Seconds_Behind_Master. Классический вариант — heartbeat-таблица: на источнике регулярно обновляется отметка времени, на реплике вы читаете её и считаете разницу.
Почему это лучше для SLI/SLO:
- метрика понятна бизнесу («задержка данных в секундах»);
- легче строить графики и алерты по порогам;
- видны микролаги и «пила», которые статус может сглаживать.
Если вы используете Percona Toolkit, то pt-heartbeat — один из самых популярных готовых инструментов для этого. Но даже самодельная heartbeat-таблица даёт огромный прирост наблюдаемости при минимальной сложности.
Типовые причины лага и что делать: чек-лист
1) Одна огромная транзакция или массовая миграция
Большие транзакции плохо параллелятся и могут надолго «заклинить» применение. Дробите батчи, избегайте гигантских INSERT ... SELECT и UPDATE без разумной стратегии разбиения. Для репликации это часто разница между минутами и часами.
2) Конфликтные записи (hot rows) и блокировки
Если все транзакции бьют в одни и те же ключи, worker’ы не дадут выигрыша: вы упрётесь в блокировки. Ищите горячие таблицы/индексы и меняйте паттерн записи (например, «распределённые» счётчики, append-only логирование) или оптимизируйте индексы.
3) Диск не вывозит relay log и flush InnoDB
Проверьте, где лежит datadir, нет ли конкурирующих нагрузок (бэкапы, архиваторы, логирование). На VDS иногда проще и надёжнее решить проблему инфраструктурно: выбрать тариф/диск с меньшей latency, выделить MySQL отдельный том, уменьшить фоновые задачи в пиковые окна.
4) Недостаток CPU (или неверный параллелизм)
RBR, сложные вторичные индексы и высокая частота коммитов требуют CPU. Слишком высокий replica_parallel_workers может дать контеншн, слишком низкий — не позволит догонять. Подбирайте по наблюдениям: загрузка CPU, run queue, активность worker’ов, время применения транзакций.
5) Сеть и semi-sync
При включённом semi-sync лаг и деградация реплики могут влиять на latency записи на источнике. Мониторьте RTT/подтверждения и заранее определите политику деградации (таймауты, fallback), чтобы не «заморозить» запись.
Мини-ранбук: что делать, когда lag уже случился
Проверьте, что репликация жива: оба потока running, нет ошибок в
Last_IO_Error/Last_SQL_Error.Определите тип проблемы: I/O-thread не успевает читать binlog или apply не успевает применять relay log.
Проверьте, не «висит» одна жирная транзакция или блокировка (частая причина резкого скачка лага).
Посмотрите диск: latency/очередь, свободное место, динамику
Relay_Log_Space.Если проблема хроническая — включайте/тюньте parallel replication, пересматривайте размер транзакций и профиль binlog.
Итоги: как свести replication lag к управляемому уровню
Лаг репликации — это баланс между тем, сколько изменений генерирует источник, и тем, как быстро реплика их применяет. На VDS чаще всего упираются в I/O и настройки параллелизма. Начните с корректного измерения лага (heartbeat), затем разделите проблему на «читать» vs «применять», проверьте GTID-наборы и динамику relay log, и только потом меняйте настройки (parallel replication, flush-профиль, формат binlog) и инфраструктуру.
Стабильная репликация получается там, где есть метрики, алерты и понятный ранбук — тогда lag превращается из «пожара» в управляемую величину.


