Миф: репликация спасёт от случайного удаления данных
«У нас 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 ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →