Асинхронная репликация. Лидер фиксирует транзакцию у себя (WAL на диск), отвечает клиенту и отправляет изменения репликам в фоне. Задержка записи минимальна, реплики не влияют на доступность записи. Но если лидер умер безвозвратно, а реплика отставала, записи, подтверждённые клиентам, потеряны. При переключении на такую реплику «сделанные» заказы исчезают, а старый лидер, вернувшись, может их «вспомнить» — конфликт.
Синхронная репликация. Лидер ждёт, пока реплика подтвердит получение (или запись на диск, или даже применение — уровни synchronous_commit в PostgreSQL: remote_write, on, remote_apply). Потерь при отказе одного узла нет — RPO = 0. Цена:
- задержка каждого коммита + RTT до реплики. В пределах дата-центра — доли миллисекунды, между городами — 10+ мс;
- если единственная синхронная реплика недоступна, запись останавливается — доступность записи ниже, чем у одной машины.
Компромиссы:
- полусинхронная (MySQL semi-sync) — ждём подтверждения, что реплика получила событие, но не применила; при таймауте откатываемся к асинхронной (и в этот момент снова возможны потери);
- кворумная — ждём K из N реплик (
ANY 1 (r1, r2) в PostgreSQL, min.insync.replicas и acks=all в Kafka). Отказ одной реплики не останавливает запись, а данные на двух узлах; - консенсус (Raft, Paxos) — запись подтверждается большинством, лидер выбирается так, чтобы он гарантированно имел все подтверждённые записи (etcd, CockroachDB, Patroni с синхронным режимом).
RPO и RTO — язык требований: RPO (recovery point objective) — какое окно данных допустимо потерять (0 для платежей, минуты для аналитики), RTO (recovery time objective) — за сколько восстановиться. Синхронная репликация отвечает за RPO, автоматический failover — за RTO.
Практика: синхронная реплика в том же регионе (другой зоне доступности) плюс асинхронные — в другом регионе для катастроф; автоматический failover с проверкой, что новый лидер не потерял подтверждённое (Patroni, Orchestrator).