MAATRIX / Блог / Миф: репликация спасёт от случайного удаления данных

Миф: репликация спасёт от случайного удаления данных

MAATRIX

«У нас master-replica, если что — переключимся на реплику» — эту фразу можно услышать перед почти каждым инцидентом с потерей данных. Проблема в том, что репликация решает задачу отказа железа, а не задачу человеческой ошибки: она с той же скоростью, с какой защищает от падения диска, доставляет на все копии и ваш случайный DROP TABLE. К концу августа 2026 года это по-прежнему одно из самых живучих заблуждений в эксплуатации баз данных, и разбираться в нём стоит до инцидента, а не после.

Почему это вообще звучит правдоподобно

Интуиция понятна: у вас есть несколько копий данных на разных серверах, значит, если что-то пойдёт не так на одной машине, данные останутся на другой. Это верно для аппаратных сбоев — сгорел диск, упал сервер, отвалилась сеть в дата-центре. Реплика в такой ситуации действительно спасает: вы теряете один физический узел, но не теряете данные и продолжаете работу с оставшейся копии.

Ошибка в рассуждении в том, что репликация переносится с этой узкой задачи на совершенно другую — защиту от логических ошибок. А логическая ошибка (неверный запрос, баг в коде, удаление не той таблицы) — это не повреждение диска, а *корректно выполненная команда с неверным смыслом*. Реплика не умеет отличать «правильное» изменение от «ошибочного»: она просто повторяет то, что произошло на мастере, потому что в этом и заключается её работа.

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

Как на самом деле работает репликация

Механика зависит от СУБД, но принцип один: мастер записывает изменения в журнал транзакций, реплика читает этот журнал и применяет те же изменения у себя.

В PostgreSQL это WAL (Write-Ahead Log). При потоковой репликации (streaming replication) реплика подключается к мастеру как клиент репликации и получает поток WAL-записей почти в реальном времени:

# на мастере, postgresql.conf
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10

# pg_hba.conf — разрешить подключение реплики
host replication replicator 10.0.0.0/24 scram-sha-256
# на реплике — создание через pg_basebackup
pg_basebackup -h 10.0.0.10 -D /var/lib/postgresql/16/main \
  -U replicator -P -R -X stream

Флаг -R создаёт standby.signal и прописывает параметры подключения к мастеру — с этого момента реплика начинает непрерывно получать и применять WAL. Подробнее о механике и о том, что такое отставание (лаг) реплики, — в отдельном разборе: как работает репликация и отставание реплики.

В MySQL/MariaDB похожий механизм называется binlog-репликацией: мастер пишет изменения в бинарный лог, реплика через I/O thread копирует события в собственный relay log, а SQL thread применяет их к своим данным. В GTID-режиме события идентифицируются глобально уникальными идентификаторами транзакций, что упрощает переключение между источниками, но сути не меняет: применяется то же самое, что случилось на мастере, в том же порядке.

Ключевой момент для понимания мифа — что именно попадает в журнал репликации. Туда попадает не «намерение» пользователя, а результат выполнения SQL-команды на уровне строк или операторов. Если вы выполнили DROP TABLE orders;, в журнал репликации попадает событие «удалить таблицу orders», и реплика выполнит ровно то же самое.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Почему DROP TABLE долетает до реплики за секунды

Возьмём конкретный сценарий. Инженер зашёл на боевую базу, чтобы почистить тестовые записи, и выполнил:

DELETE FROM orders WHERE created_at < '2020-01-01';

Но забыл про условие в другом окне терминала и повторил похожую команду уже без WHERE:

DELETE FROM orders;

На мастере с PostgreSQL эта транзакция коммитится, событие удаления пишется в WAL и почти немедленно передаётся на реплику через walsender/walreceiver. При типичной задержке потоковой репликации в локальной сети — доли секунды, максимум единицы секунд при высокой нагрузке или отставании по сети. У инженера физически нет времени среагировать: пока он понимает, что натворил, таблица orders уже пуста и на мастере, и на всех синхронных и асинхронных репликах.

То же самое с MySQL: SQL thread реплики применяет события из relay log практически сразу после того, как они туда попали, если реплика не отстаёт по нагрузке. Задержка репликации (replication lag) в норме измеряется секундами, а не часами — то есть она защищает от долгих недоступностей мастера, но не даёт «окна», чтобы успеть остановить распространение ошибки.

Важно понимать, что это не баг репликации, а её единственно правильное поведение. Если бы реплика применяла изменения выборочно или с большой произвольной задержкой, она перестала бы быть надёжной копией для отказоустойчивости — именно в предсказуемой и быстрой доставке изменений и состоит её ценность. Проблема не в механизме, а в ожиданиях: реплика была спроектирована для другой задачи.

Отдельно стоит сказать про синхронную репликацию (synchronous_commit = on/remote_apply в PostgreSQL) — она ещё усиливает иллюзию защищённости, потому что подтверждение коммита на мастере ждёт применения на реплике. На деле это делает ошибку ещё быстрее: транзакция считается завершённой только после того, как она уже применена на реплике, то есть отменить её на этом этапе невозможно в принципе.

Настоящее назначение репликации

Чтобы не разочароваться в репликации, полезно чётко зафиксировать, для чего она действительно нужна — она отлично решает эти задачи, просто не решает задачу логических ошибок.

Отказоустойчивость при аппаратном сбое. Если сервер с мастером выходит из строя — сгорел блок питания, отказал RAID-контроллер, пропала связность в дата-центре, — реплика позволяет быстро переключиться на неё как на новый мастер (failover) и продолжить работу с минимальным простоем. Именно для этого сценария репликация была придумана изначально, и здесь она действительно окупает себя.

Распределение нагрузки на чтение. Реплики можно использовать как источник для SELECT-запросов, разгружая мастер, который остаётся единственной точкой записи. Это классический паттерн read replica для отчётов, аналитики, тяжёлых выборок — нагрузка уходит с продакшн-мастера, а пользователи не замечают разницы, если приложение умеет корректно направлять запросы.

Географическое распределение. Реплика в другом регионе снижает задержку для чтения у удалённых пользователей и может служить холодным резервом на случай проблем с целым дата-центром.

Плавные миграции и обновления. Переключение на реплику удобно использовать при плановом обслуживании мастера — патчи ядра, апгрейд версии СУБД, замена железа — без длительного простоя.

Ни одна из этих задач не связана с защитой от логической ошибки — во всех сценариях предполагается, что данные на мастере корректны, а реплика лишь физически доступна в нужный момент.

УгрозаСпасает репликацияСпасает бэкап с PITR
Отказ диска/сервера мастераДаЧастично (с потерей времени на восстановление)
Сетевая недоступность дата-центраДа (при географическом разнесении)Нет (без offsite-хранения)
DROP TABLE / DELETE без WHEREНет — реплика повторит ошибкуДа
Баг в коде, испортивший данные за N часовНетДа, если PITR покрывает нужный момент
Шифровальщик, добравшийся до продакшн-сервераНет, если реплика доступна с того же сервераДа, если бэкап изолирован (offline/immutable)

Что реально защищает от логической ошибки

Единственный механизм, который действительно даёт возможность «отмотать назад» к состоянию до ошибки, — это резервные копии с возможностью восстановления на точку во времени (Point-in-Time Recovery, PITR).

Идея PITR: у вас есть базовая копия данных (full backup) плюс непрерывный архив журнала транзакций (WAL-архив в PostgreSQL, binlog-архив в MySQL) за период после этой копии. Восстановление состоит из двух шагов — разворачивается базовая копия, а затем журнал проигрывается вперёд до заданного момента времени, но не дальше него.

Для PostgreSQL связка обычно выглядит так: pgbackrest или wal-g для базовых копий плюс непрерывная архивация WAL:

# postgresql.conf — включаем архивацию WAL
archive_mode = on
archive_command = 'pgbackrest --stanza=main archive-push %p'

Восстановление на момент времени незадолго до ошибочного DELETE:

# recovery на отдельном сервере/после restore базовой копии
pgbackrest --stanza=main --type=time \
  --target="2026-08-29 14:32:00+03" restore

Ключевой параметр — --type=time с точным временем: журнал транзакций проигрывается вплоть до этой отметки и останавливается, не доходя до разрушительной команды. Итоговое состояние базы — ровно то, что было за секунду до ошибки, без часов простоя и без потери всех данных, накопленных с момента последнего полного бэкапа.

В MySQL логика аналогичная, только через binlog: восстанавливаете последний полный бэкап (например, через Percona XtraBackup), затем проигрываете binlog-события mysqlbinlog с ограничением по времени или по позиции, исключая транзакцию с ошибочным запросом:

mysqlbinlog --start-datetime="2026-08-29 00:00:00" \
  --stop-datetime="2026-08-29 14:31:59" \
  binlog.000045 | mysql -u root -p

Практический вывод для инфраструктуры: если у вас есть только репликация без архивации журнала транзакций и без регулярных полных копий, PITR у вас нет — а значит, нет и защиты от логической ошибки. Общий порядок восстановления из копии, включая типичные грабли, разобран в статье восстановление базы данных из бэкапа: практика, а базовый принцип «сколько копий и где хранить» — в материале про правило 3-2-1 для бэкапов: применительно к базам данных это правило касается именно полных копий плюс архива журналов, а не количества реплик.

Delayed replica: дешёвая страховка на стороне репликации

Есть промежуточное решение, которое иногда путают с PITR, но которое на деле его дополняет, а не заменяет, — реплика с искусственной задержкой применения изменений (delayed replica).

Идея простая: обычная реплика применяет изменения из журнала практически сразу, а delayed replica намеренно откладывает применение на фиксированный интервал — например, на 4-6 часов. Если на мастере происходит катастрофа вроде DROP TABLE, у вас есть окно в эти самые 4-6 часов, чтобы заметить проблему и остановить репликацию на delayed replica *до того*, как разрушительная команда до неё доедет.

В PostgreSQL это настраивается одним параметром на реплике:

# postgresql.conf на delayed replica
recovery_min_apply_delay = '4h'

Реплика продолжает получать WAL в реальном времени, но применяет его с отставанием на заданный интервал. Если мониторинг заметил массовое удаление данных, вы останавливаете recovery на delayed replica командой pg_wal_replay_pause() — и получаете консистентное состояние базы за несколько часов до инцидента, без разворачивания бэкапа с нуля.

В MySQL аналогичный механизм задаётся при настройке репликации:

CHANGE REPLICATION SOURCE TO SOURCE_DELAY = 14400; -- 4 часа в секундах

Важные оговорки, чтобы не создать ложное чувство безопасности:

  • Delayed replica — это дополнение к бэкапам с PITR, а не замена. Она не хранится долго, не защищает от сбоя всего кластера хранения и требует ручного или автоматизированного вмешательства в момент инцидента — если никто не заметил проблему за 4-6 часов, окно закрывается, и delayed replica применит то же удаление.
  • Задержку нужно подбирать по реальному времени реакции команды на алерты, а не наугад — слишком короткая не даёт запаса, слишком длинная увеличивает расхождение с продакшном и цену хранения WAL за этот период.
  • Delayed replica не отменяет необходимость мониторить сам факт лага и его причину — если реплика отстаёт непреднамеренно из-за нагрузки, а не из-за настройки, это уже отдельная проблема, а не задуманная страховка.

Комбинация «обычная реплика (для failover) + PITR-бэкапы (для логических ошибок) + delayed replica (для быстрого отката без разворачивания полного бэкапа)» закрывает практически весь спектр сценариев потери данных: от отказа диска до ошибки в коде и до случайного DROP DATABASE со стороны человека.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Если реплика синхронная, она хотя бы задержит фиксацию ошибочной транзакции?

Нет, будет обратный эффект: при synchronous_commit = remote_apply мастер подтверждает коммит клиенту только после того, как изменение уже применено на реплике — то есть к моменту, когда вы увидели «успех» команды, оно уже необратимо и на реплике тоже.

А если сразу разорвать соединение реплики при аварии — успеем остановить распространение?

Теоретически да, но на практике времени на реакцию — доли секунды или секунды при обычной потоковой репликации, этого недостаточно для ручного вмешательства. Именно поэтому нужна delayed replica с осмысленным интервалом, а не расчёт на скорость реакции человека.

Можно ли сделать delayed replica вместо архивации WAL для PITR?

Не рекомендуется как единственное решение: у delayed replica фиксированное окно (например, 4 часа), а PITR с архивом WAL позволяет откатиться на любой момент в пределах retention бэкапов — недели или месяцы. Используйте оба механизма вместе.

Реплика в другом дата-центре защищает от шифровальщика на продакшн-сервере?

Не полностью. Если вредонос получил доступ к учётным данным приложения и права на запись, он так же успешно испортит данные и на мастере, и через репликацию — на всех подключённых репликах. От такого сценария защищают offline- или immutable-бэкапы, изолированные от боевой инфраструктуры.

Сколько стоит держать WAL-архив для PITR на небольшом проекте?

Порядок величины зависит от интенсивности записи и глубины хранения; для среднего проекта это обычно заметно дешевле, чем ещё один полноценный сервер под реплику, но точный объём стоит прикинуть по факту генерации WAL на вашей нагрузке, а не по чужим цифрам.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →