Синхронная репликация и выборы лидера (Raft)

Рейтинг: 52.9% · 8 голосов
Исчерпывающий курс по Tarantool 3.x: модель данных, движки memtx и vinyl, Lua и файберы, транзакции и MVCC, SQL, конфигурация (box.cfg и декларативная 3.x), репликация и Raft, шардирование vshard, эксплуатация, безопасность. 47 уроков со схемами.
Ответить
Аватара пользователя
denis_tnt
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Синхронная репликация и выборы лидера (Raft)

Сообщение denis_tnt »

Оглавление курса (47)
  1. Что такое Tarantool: in-memory СУБД и сервер приложений
  2. Архитектура изнутри: процесс, потоки, event-loop
  3. Установка и первый запуск: tt CLI, пакеты, Docker
  4. Интерактив: консоль, admin-консоль, первые команды
  5. Спейсы и кортежи: форматы, типы данных
  6. Типы индексов и их применимость
  7. Движки хранения: memtx vs vinyl
  8. DDL: схема, создание спейсов и индексов, миграции
  9. DML и выборки: insert/update/upsert, итераторы
  10. Персистентность: WAL, снапшоты, recovery
  11. Внутренности memtx: аллокаторы slab/arena, память
  12. Внутренности vinyl: LSM, компакция, тюнинг
  13. Lua и LuaJIT в Tarantool: box, модули, rocks
  14. Файберы: кооперативная многозадачность, каналы
  15. Транзакции: ACID, изоляция, MVCC
  16. Хранимые процедуры, модули, организация приложения
  17. net.box: удалённые вызовы, async
  18. Пулы соединений, балансировка, реконнект
  19. Ошибки и диагностика: box.error, pcall
  20. Типы и сериализация: MsgPack, decimal, datetime, uuid
  21. SQL в Tarantool: возможности и связь с box
  22. SQL: таблицы, JOIN, подзапросы, представления
  23. SQL: подготовленные выражения, транзакции, Lua-интероп
  24. Классическая конфигурация box.cfg (legacy 1.x/2.x)
  25. Декларативная конфигурация 3.x: config.yaml, иерархия
  26. Роли и приложения в 3.x
  27. Централизованная конфигурация: etcd / config storage
  28. tt CLI глубоко: разработка, сборка, запуск
  29. Cartridge (официальный legacy) и миграция на 3.x
  30. Репликация: replicaset, топологии
  31. Механика репликации: WAL-стриминг, vclock
  32. Синхронная репликация и выборы лидера (Raft) (вы здесь)
  33. Жизненный цикл узла: bootstrap, join, rejoin
  34. vshard: router/storage, виртуальные бакеты
  35. Решардинг и rebalancing бакетов
  36. Запросы поверх шардов: map-reduce, crud
  37. Мониторинг: метрики, Prometheus, Grafana
  38. Логирование и аудит
  39. Бэкапы и восстановление
  40. Безопасность: аутентификация, RBAC, TLS
  41. Производительность: профилирование, тюнинг
  42. Обновления: схема, rolling upgrade
  43. Деплой в продакшен: Docker, топология (официальные паттерны)
  44. Администрирование через официальный TCM (Tarantool Cluster Manager)
  45. Коннекторы: Python, Go, Java
  46. Ключевые модули (rocks): crud, metrics, queue, expirationd
  47. Capstone: шардированный отказоустойчивый кластер
Обзор

По умолчанию репликация в Tarantool асинхронная: транзакция коммитится локально и попадает на реплики потом, без гарантий. Если мастер ответил клиенту "успех" и тут же умер, после failover на реплику транзакция может исчезнуть. Синхронная репликация решает эту проблему: транзакция не считается закоммиченной и клиент не получает ответ, пока запись не доедет до кворума узлов. Отдельно от этого работает автоматический выбор лидера на основе алгоритма Raft - он гарантирует, что в любой момент в наборе реплик не более одного писателя.

В Tarantool это две независимые подсистемы. Можно включить синхронную репликацию без выборов (лидера назначает внешний координатор), а можно включить выборы без синхронных спейсов. Полный Raft получается, когда обе включены вместе и настроены строго: все спейсы синхронные, кворум N/2+1, synchro_timeout фактически бесконечный, fencing включён.

Механика и архитектура

Лимб (limbo) - очередь синхронных транзакций. Сердце синхронной репликации - структура limbo на лидере. Когда вы коммитите транзакцию в синхронный спейс, её строки пишутся в WAL локально, но сама транзакция не подтверждается, а попадает в лимб и ждёт. Лидер рассылает её на реплики; каждая реплика, записав строки в свой WAL, шлёт обратно ACK (подтверждение vclock). Как только число подтверждений (включая саму лидера) достигает synchro_quorum, лидер пишет в WAL служебную запись CONFIRM и только тогда отвечает клиенту "ОК". CONFIRM реплицируется дальше, и реплики делают данные видимыми у себя.

Важная деталь внутренностей: CONFIRM и ROLLBACK применяются на реплике только после записи в её WAL, а не сразу по приёму. Иначе был бы баг - подтверждённые данные становятся видны, узел рестартует до окончания записи CONFIRM, и данные снова невидимы.

Правило порядка. Все транзакции (и синхронные, и асинхронные) коммитятся в том же порядке, в каком был вызван box.commit(). Асинхронная транзакция, попавшая за синхронную, ждёт её - но синхронной не становится: дождавшись CONFIRM предыдущей, она коммитится сразу, не ожидая своего кворума. Откат идёт в обратном порядке: если синхронная транзакция упала по таймауту, она откатывает и все более новые, что ждали за ней.

Raft: термы, голоса, кворум. Жизнь набора реплик делится на термы (term) - монотонно растущие числа. Каждый узел в состоянии follower, candidate или leader. Если follower не видит лидера дольше replication.timeout * 4 (нет heartbeat), он увеличивает терм, переходит в candidate, голосует за себя и рассылает запросы голосов. Узел голосует за первого попросившего в данном терме и больше в этом терме не голосует. Кто собрал кворум голосов (тот же synchro_quorum) - становится лидером и шлёт heartbeat остальным. При split vote (никто не набрал кворум) после случайного таймаута термы снова растут и round повторяется. Узлы предпочитают голосовать за тех, у кого свежее данные (больше vclock) - чтобы не потерять то, что старый лидер успел разослать. Термы и голоса персистятся на диск.

PROMOTE / DEMOTE - смена владельца лимба. Право писать синхронные транзакции принадлежит владельцу очереди. При смене лидера новый узел пишет служебную запись PROMOTE: она забирает владение лимбом и финализирует "висящие" транзакции прежнего лидера (подтверждает то, что успело набрать кворум, откатывает остальное). box.ctl.promote() делает это вручную, box.ctl.demote() - снимает владение.

Fencing - защита от двух лидеров. В классическом Raft лидер не следит за своей связностью и считает себя лидером, пока не увидит больший терм. Tarantool добавил fencing: если у лидера осталось меньше synchro_quorum живых соединений, он сам слагает полномочия (resign), становится follower и read-only. Режим soft (по умолчанию) считает связь мёртвой после 4 * replication.timeout; strict - после 2 * replication.timeout на лидере, давая больше шансов на единственного лидера.

Изображение
Синхронный коммит через кворум и выборы лидера Raft

Ключевые команды и код

Декларативная конфигурация 3.x (config.yaml) - полный Raft на трёх узлах:

Код: Выделить всё

replication:
  failover: election          # включить выборы Raft
  election_mode: candidate    # узел может стать лидером
  election_fencing_mode: soft # сам слагает полномочия при потере кворума
  synchro_quorum: 'N / 2 + 1' # формула: для 3 узлов это 2
  synchro_timeout: 1000       # сек; для строгого Raft - очень большой
  timeout: 1                  # база для heartbeat (timeout * 4)
Классический box.cfg (1.x/2.x), то же самое:

Код: Выделить всё

box.cfg{
  replication = {'node1_uri', 'node2_uri', 'node3_uri'},
  election_mode = 'candidate',
  election_fencing_mode = 'soft',
  replication_synchro_quorum = 'N / 2 + 1',
  replication_synchro_timeout = 1000,
  replication_timeout = 1,
}
Сделать спейс синхронным и проверить состояние:

Код: Выделить всё

-- синхронность задаётся ПОСПЕЙСНО
box.schema.space.create('orders', {is_sync = true})
box.space.orders:create_index('pk')

-- состояние выборов
box.info.election
-- state: leader, term: 5, vote: 1, leader: 1

-- состояние очереди синхронных транзакций
box.info.synchro
-- queue: {owner: 1, len: 0, busy: false, term: 5}
-- quorum: 2
Значения election_mode: off (только реплицирует, не голосует), voter (голосует, но не кандидат), candidate (может стать лидером), manual (лидер только через box.ctl.promote()).

Частые заблуждения и грабли
Главная ловушка: synchro_quorum меньше N/2+1. Тогда возможен split vote и ДВА лидера сразу. Пример из доков: 5 узлов, quorum=2 - node1 и node2 голосуют за node1, node3 и node4 за node5, оба побеждают. Всегда большинство: (N/2)+1.
  • Выборы без синхронной репликации почти бесполезны для сохранности данных. Если репликация асинхронная, старый лидер после потери связи продолжает коммитить асинхронные транзакции (их никто не проверяет кворумом) - получаете расхождение данных.
  • Синхронность - поспейсная, не глобальная. Только один узел (владелец лимба) может делать синхронные транзакции; топология master-slave. Анонимные реплики в кворуме не участвуют (с 2.10).
  • Смешивание sync и async опасно. Старый лидер мог закоммитить async-транзакции, которых нет ни у кого. После восстановления связи защита целостности кинет ER_SPLIT_BRAIN и заставит перебутстрапить узел.
  • synchro_quorum считает и саму лидера. Для 3 узлов quorum=2 означает "лидер плюс одна реплика", а не "две реплики помимо лидера".
  • Менять quorum при добавлении узлов нужно ЗАРАНЕЕ - обновить значение на всех существующих узлах до подключения нового, иначе на момент перехода большинство посчитается неверно.
  • election требует full mesh - прямое соединение между каждой парой узлов, иначе голосование не работает.
Мини-лаба

Поднимите набор из 3 узлов с failover: election и synchro_quorum: 'N / 2 + 1'. Создайте синхронный спейс, вставьте строку, на каждом узле выполните box.info.synchro и box.info.election - найдите owner лимба, term и quorum. Затем остановите текущего лидера (узнать его можно по box.info.election.state == 'leader'), подождите несколько секунд и проверьте box.info.election на оставшихся узлах: убедитесь, что term увеличился и появился новый leader. Верните прежний узел и проверьте, что он стал follower и read-only.

Контрольные вопросы
  • Что такое лимб (limbo) и в какой момент лидер пишет запись CONFIRM в WAL?
  • Почему synchro_quorum обязан быть не меньше N/2+1, и к чему ведёт меньшее значение при выборах?
  • Чем режим fencing soft отличается от strict и какую проблему классического Raft он закрывает?
  • Что делает запись PROMOTE при смене лидера и какая команда вызывает её вручную?
👍1 ❤️1 🔥2 😄 🤔2
Ответить
← Предыдущая глава
Механика репликации: WAL-стриминг, vclock
Следующая глава →
Жизненный цикл узла: bootstrap, join, rejoin

Все главы курса «Tarantool: in-memory СУБД и сервер приложений с нуля до продакшена»

Поделиться темой: ✈ Telegram VK
  • Похожие темы

Вернуться в «Tarantool: СУБД и сервер приложений»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость