Репликация: replicaset, топологии

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

Репликация: replicaset, топологии

Сообщение 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 - это механизм, при котором несколько экземпляров (instances) работают на копиях одних и тех же баз данных и держат их синхронными. Группа таких экземпляров называется replica set (репликасет). У каждого экземпляра внутри репликасета есть роль: master (доступен на запись) или replica (только чтение). Из ролей складываются топологии: классическая мастер-реплика и мастер-мастер (multi-master). В этом уроке разбираем не столько "как настроить", сколько как это работает внутри - откуда берётся синхронность, почему full mesh, и почему мастер-мастер не панацея.

Механика: WAL, LSN и vclock

Фундамент репликации - это write-ahead log (WAL). Каждое изменение данных (INSERT, UPDATE, DELETE) записывается в WAL как отдельная запись и получает монотонно растущий номер - LSN (log sequence number). Реплика не получает готовые строки; она непрерывно тянет (fetch) и применяет (apply) записи WAL мастера. Репликация в Tarantool row-based: каждый запрос детерминирован и работает с одним кортежем (tuple).
Важная тонкость: вызовы хранимых функций в WAL не пишутся. Пишутся фактические data-change операции, которые эта Lua-функция выполнила. Поэтому возможный недетерминизм Lua (random, время, сеть) не ломает репликацию - реплики применяют уже зафиксированный результат, а не код.
Чтобы понять, кто чьи изменения уже применил, нужны идентификаторы. У всего репликасета есть replica set UUID, у каждого экземпляра - instance UUID (глобально уникальный) и короткий instance ID (уникальный внутри репликасета, целое число). UUID хранится в системном спейсе

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

box.space._cluster
. Instance ID нужен, чтобы каждую строку применить ровно один раз - в WAL рядом с LSN пишется ID экземпляра, на котором запись родилась. Это и есть основа multi-master: строки от разных мастеров не перепутаются.

Состояние репликации отражает vclock (vector clock) - вектор "последний применённый LSN по каждому instance ID":

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

tarantool> box.info.vclock
---
- {1: 827, 2: 584}
...
-- от instance 1 применено 827 записей, от instance 2 - 584
Когда реплика подключается, она проходит стадии: bootstrap (первичная инициализация всего набора - мастер создаёт checkpoint-файлы и раздаёт их), join (реплика скачивает начальный снимок и регистрируется в _cluster) и follow (бесконечное дочитывание WAL мастера). Статус видно через

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

box.info.replication[n].upstream.status
- в норме там .
По умолчанию репликация асинхронная: мастер ответил клиенту "ок" сразу после локального коммита, не дожидаясь реплик. Если мастер тут же умрёт, после failover транзакция может "исчезнуть". Синхронную репликацию (per-space, опция is_sync) разбираем в отдельном уроке.
Изображение
Топологии Tarantool: мастер-реплика и мастер-мастер full mesh

Топологии и роли

Роль задаётся параметром read_only (в декларативном конфиге 3.x -

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

database.mode: ro|rw
). Рекомендация: ro для всех, кроме одного экземпляра.

Мастер-реплика. Один пишет, остальные читают. Что меняется на мастере - видно на репликах, обратно - нет. Даёт две выгоды: failover (реплика подхватит, если мастер упал) и балансировку чтения. В 3.x за переключение лидера отвечает

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

replication.failover
: (лидер задан явно через ),

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

election
(Raft-голосование) или

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

supervised
(внешний координатор).

Мастер-мастер (multi-master). Оба экземпляра в режиме rw, оба пишут, изменения видны в обе стороны. Настраивается через

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

replication.failover: off
и

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

database.mode: rw
для всех. Топология - full mesh: каждый соединён с каждым.

Топология соединений задаётся параметром

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

replication
(список адресов). Рекомендованная - full mesh, потому что облегчает failover и гарантирует, что все знают UUID друг друга. Tarantool сам применит каждую строку лишь однажды. Каскад (реплика реплики) не рекомендуется: крайние экземпляры не видят друг друга и не получают записей в _cluster, из-за чего мастер потом откажет им в подключении. Ring (кольцо) поддерживается. Если очень нужен каскад - сначала собирают кольцо (чтобы все обменялись UUID), затем разрывают в нужном месте.

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

Топология        Кто пишет      Соединения        Заметка
---------------  -------------  ----------------  ----------------------
master-replica   один master    mesh              рекомендуется
master-master    все instances  full mesh         нужна коммутативность
ring             все/один       кольцо            поддерживается
cascade          -              цепочка           НЕ рекомендуется
Предел: максимум 32 экземпляра в full mesh.
Ключевой код (3.x, декларативный config.yaml)

Фрагмент конфигурации мастер-мастер из двух узлов:

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

credentials:
  users:
    replicator:
      password: 'topsecret'
      roles: [replication]

iproto:
  advertise:
    peer:
      login: replicator

replication:
  failover: off

groups:
  group001:
    replicasets:
      replicaset001:
        instances:
          instance001:
            database:
              mode: rw
            iproto:
              listen:
              - uri: '127.0.0.1:3301'
          instance002:
            database:
              mode: rw
            iproto:
              listen:
              - uri: '127.0.0.1:3302'
Проверка, что оба узла пишущие и связь жива:

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

-- на каждом узле
box.info.ro            --> false  (узел доступен на запись)
box.info.replication   --> upstream.status: follow
                           downstream.status: follow
box.info.vclock        --> совпадает на обоих узлах
Частые заблуждения и грабли
  • "Мастер-мастер ускоряет запись." Нет. Запись в любом случае реплицируется на остальных; вы лишь распределяете точки входа. Зато получаете риск конфликтов.
  • "Можно писать одно и то же на обоих мастерах." Безопасно, только если все изменения коммутативны - результат не зависит от порядка применения. Append-only безопасен. DELETE по TTL обычно тоже. А вот UPDATE с присваиванием или инкрементом не коммутативен и разъедет реплики.
  • Конфликт дубликата ключа. Если на двух мастерах вставить кортеж с одним primary key, при встрече потоков

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

    upstream.status
    станет с ошибкой "Duplicate key exists". Лечится reseed (rebootstrap) отстающей реплики и перезапуском репликации.
  • Split-brain. Два независимых лидера на запись (например, при ошибочном

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

    synchro_quorum
    ниже N/2+1) приводят к ошибке

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

    ER_SPLIT_BRAIN
    и требуют rebootstrap. Защита целостности срабатывает при восстановлении связи.
  • Каскад "просто чтобы сэкономить трафик". Приводит к проблемам с _cluster UUID и отказам подключения. Используйте mesh.
Превентивно конфликты решают триггером

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

before_replace
на спорном спейсе: внутри сравнивают старый и новый кортеж и выбирают победителя (или сливают записи).

Мини-лаба
  • Поднимите репликасет из двух узлов через (см. конфиг выше). На instance001 создайте спейс и индекс, вставьте пару кортежей. На instance002 выполните

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

    box.space.<name>:select()
    и убедитесь, что данные приехали. Затем вставьте новые кортежи уже на instance002 и проверьте их на instance001. Сравните

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

    box.info.vclock
    на обоих узлах - значения должны совпасть. Бонус: остановите instance002, вставьте на instance001 кортеж с ключом 5, отдельно на остановленном instance002 - другой кортеж с ключом 5, запустите обоих и посмотрите, как

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

    upstream.status
    перейдёт в .
Контрольные вопросы
  • Что именно реплика тянет с мастера - готовые строки таблицы или записи WAL? Почему вызовы Lua-функций не реплицируются как код?
  • Зачем в каждой записи WAL хранится instance ID, и как это связано с возможностью multi-master?
  • Что показывает

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

    box.info.vclock
    и как по нему понять, что узлы синхронизированы?
  • Почему мастер-мастер безопасен для append-only, но опасен для UPDATE-инкремента? Сформулируйте требование коммутативности.
👍3 ❤️2 🔥2 😄 🤔
Ответить
← Предыдущая глава
Cartridge (официальный legacy) и миграция на 3.x
Следующая глава →
Механика репликации: WAL-стриминг, vclock

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

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

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

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

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