Репликация баз данных
| Цели | отказоустойчивость, масштабирование чтения, близость к пользователям, резервирование |
|---|---|
| Схемы | с одним ведущим, с несколькими ведущими, без ведущего |
| Режимы | синхронный, асинхронный, полусинхронный |
| Уровни | физическая (журнал WAL, страницы), логическая (изменения строк) |
| PostgreSQL | потоковая репликация (9.0, 2010), логическая (10, 2017) |
| Управление отказами | Patroni, Orchestrator, облачные сервисы |
Репликация базы данных — поддержание копий одних и тех же данных на нескольких серверах. Сервер, принимающий изменения, называют ведущим (primary, leader; устаревшее — master), а серверы-копии — репликами (replica, follower, standby). Изменения ведущего непрерывно передаются репликам и применяются там.
Репликация решает несколько задач сразу: отказоустойчивость (при отказе ведущего реплика становится новым ведущим), масштабирование чтения (запросы на чтение распределяются по репликам), близость к пользователям (реплика в другом регионе) и разгрузку ведущего от отчётов и резервного копирования.
Схемы репликации
| Схема | Как устроена | Плюсы | Минусы |
|---|---|---|---|
| Один ведущий | все записи идут на ведущий, реплики только читают | просто, нет конфликтов записи | запись не масштабируется, переключение при отказе |
| Несколько ведущих | запись принимают несколько узлов, обычно в разных дата-центрах | запись локально в каждом регионе | конфликты одновременных изменений |
| Без ведущего | клиент пишет и читает с нескольких узлов по кворуму | высокая доступность | сложная согласованность |
Схема с одним ведущим — стандарт для PostgreSQL, MySQL и большинства реляционных СУБД. Репликация без ведущего с кворумами записи и чтения описана в статье Amazon о Dynamo (2007) и используется в Cassandra и ScyllaDB.[5]
Синхронная и асинхронная
- Асинхронная — ведущий подтверждает запись клиенту, не дожидаясь реплик. Быстро, но при внезапном отказе ведущего последние подтверждённые транзакции могут не успеть дойти до реплики и потеряются.
- Синхронная — ведущий ждёт подтверждения хотя бы от одной реплики. Данные не теряются, но каждая запись медленнее, а недоступность реплики может остановить запись.
- Полусинхронная — компромисс: синхронно с одной репликой, асинхронно с остальными.
В PostgreSQL режим задаётся параметрами synchronous_standby_names и synchronous_commit, причём выбирать можно даже для отдельной транзакции.[1]
Физическая и логическая
- Физическая (потоковая) репликация передаёт журнал упреждающей записи (WAL) — побайтовые изменения страниц. Реплика — точная копия всего кластера, той же мажорной версии СУБД. В PostgreSQL доступна с версии 9.0 (2010).
- Логическая репликация передаёт изменения на уровне строк (вставка, обновление, удаление) для выбранных таблиц. Позволяет реплицировать часть данных, между разными мажорными версиями (для обновления почти без простоя) и в другие системы. В PostgreSQL — с версии 10 (2017) по модели публикаций и подписок.[2]
В MySQL изменения передаются через двоичный журнал (binlog) в построчном или операторном формате, с глобальными идентификаторами транзакций (GTID) для надёжного переключения.[3]
-- PostgreSQL: логическая репликация одной таблицы
-- на источнике
CREATE PUBLICATION orders_pub FOR TABLE orders;
-- на получателе
CREATE SUBSCRIPTION orders_sub
CONNECTION 'host=primary dbname=shop user=replicator'
PUBLICATION orders_pub;
Отставание реплик
При асинхронной репликации реплика отстаёт от ведущего — обычно на миллисекунды, но под нагрузкой или при долгих запросах на реплике — на секунды и минуты. Отсюда аномалии, с которыми сталкивается приложение:[4]
- Чтение собственных записей. Пользователь сохранил профиль, страница перезагрузилась с реплики — и изменения «пропали». Решение: читать данные, которые пользователь только что менял, с ведущего.
- Монотонность чтений. Последовательные запросы попадают на разные реплики и видят данные «то новее, то старее». Решение: привязать сессию к одной реплике.
Отставание нужно мониторить: в PostgreSQL — через представление pg_stat_replication на ведущем.
Переключение при отказе
Если ведущий вышел из строя, одну из реплик нужно повысить до ведущего и перенаправить на неё клиентов. Делать это вручную долго, а автоматически — рискованно: при сетевом разделении два узла могут одновременно счесть себя ведущими (split brain) и принять конфликтующие записи. Поэтому для автоматического переключения используют инструменты с консенсусом через etcd, Consul или ZooKeeper — например, Patroni для PostgreSQL — или управляемые облачные базы данных.[6]
Репликация — не резервная копия
Реплика мгновенно повторяет и ошибочные изменения: случайный DELETE без WHERE или повреждение данных приложением дойдут до всех копий за доли секунды. Защиту от этого дают только резервные копии с историей и восстановлением на момент времени (см. Бэкапы: правило 3-2-1). Отложенная реплика (с задержкой применения на несколько часов) — полезное, но не заменяющее бэкапы дополнение.
Репликация и шардирование
Репликация копирует все данные на несколько узлов и масштабирует чтение, но не объём записи и не размер данных на одном узле. Для этого данные делят на части — шарды, — каждая из которых обычно ещё и реплицируется. Компромиссы между согласованностью и доступностью в таких системах описывает CAP-теорема.