Реплики дают отказоустойчивость и разгрузку чтений, Sentinel — автоматическое переключение при падении мастера. Но фундамент всего этого — асинхронность, из которой следуют и скорость, и риски.
Асинхронная репликация
Мастер шлёт поток команд репликам, не дожидаясь подтверждения, и отвечает клиенту сразу. Отсюда:
- Быстро: запись не платит за сеть до реплик;
- Можно потерять хвост: запись, подтверждённая клиенту, могла не доехать до реплики. Мастер умирает → реплика становится мастером → этот хвост исчез;
- Реплики могут отставать: чтение с реплики = чтение данных «недавнего прошлого» (stale reads). Для «записал и сразу прочитал» читайте у мастера.
Настройка реплики
# На реплике:
replicaof 10.0.0.1 6379
masterauth <пароль-мастера> # если на мастере requirepass
replica-read-only yes # дефолт; реплики только читают
# На мастере — минимальные гарантии записи:
min-replicas-to-write 1
min-replicas-max-lag 10
# мастер отказывается принимать записи, если нет живой реплики с лагом ≤10с
Полезное про механику:
- Репликация инкрементальная (поток команд) + ресинхронизация через backlog
(
repl-backlog-size: отставание в пределах буфера — быстрое PSYNC, иначе полный RDB-трансфер); - Истечения TTL реплицируются явными DEL от мастера — время на реплике не искажает смерть ключей;
- Лаг =
master_repl_offset−slave_repl_offset(INFO replication); мониторьте его.
WAIT: синхронное подтверждение, когда очень надо
SET critical:payment-id 42
WAIT 1 200 # ждать подтверждения от 1 реплики, таймаут 200 мс
WAIT гарантирует, что команда достигла N реплик — reduces окно потери до нуля для конкретной записи ценой латентности. Используйте точечно для критичных операций, а не на весь трафик.
Sentinel: автоматический failover
Sentinel — отдельные процессы-наблюдатели (минимум 3, распределённые по хостам), которые:
- пингуют мастер и реплики; при недоступности дольше
down-after-millisecondsмастера считают «объективно мёртвым» по кворуму (quorum); - выбирают реплику с наименьшим лагом и повышают её в мастера;
- перенастраивают остальные реплики и переадресуют клиентов (клиенты, умеющие Sentinel-протокол, сами узнают новый адрес).
# sentinel.conf (на каждом из 3 хостов)
sentinel monitor mymaster 10.0.0.1 6379 2 # 2 = кворум из 3 сентинелов
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
⚠️ Sentinel минимизирует простой, но не отменяет асинхронность: последние миллисекунды-секунды записей при падении мастера всё ещё могут потеряться. Требования «ноль потерь при failover» к обычному Redis не выполнимы без WAIT и компромиссов по латентности.
Сплит-брейн
Сетевая изоляция: старый мастер жив, но отделён от реплик и Sentinel'ов. Без защиты он продолжает
принимать записи, которые потом потеряются. Защита — уже знакомая
min-replicas-to-write: изолированный мастер без живых реплик перестаёт писать.
Чтение с реплик: за и против
- За: разгрузка мастера на read-heavy трафике, геораспределённость;
- Против: stale reads, двойная книга «кто куда ходит», при failover клиентские ошибки READONLY — нужен переподключатель;
- Часто лучший масштабирующий ответ — не реплики на чтение, а правильный кеш (модуль 2).
Практика
- docker-compose: мастер + 2 реплики. Занесите данные, убедитесь, что они видны с реплик, и что запись на реплику отклоняется.
- Добавьте 3 Sentinel, убейте мастер — проследите выборы в логах Sentinel'ов и переподключение клиента (redis-py/lettuce умеют Sentinel нативно).
- Замерьте окно потери: с записью через WAIT и без —
kill -9мастера сразу после SET, что вернула новая копия данных. - Оторвите мастер от реплик (iptables/docker network) с min-replicas-to-write — проверьте, что записи блокируются.
Мини-квиз
1. Почему failover может потерять подтверждённые записи? — Репликация асинхронна: хвост команд мог не доехать до новой копии.
2. Зачем min-replicas-to-write? — Чтобы изолированный/бездымный мастер не принимал записи «в пустоту» (анти-сплит-брейн).
3. Клиент читает с реплики сразу после записи. Что видит? — Возможно старые данные (stale read); для read-after-write читайте мастера.