Tarantool CE/EE Documentation portal logo
Помощь
Обновлена 15 сентября 2026 г. в 08:55

Архитектура репликации

Механизм репликации

Обзор

Набор экземпляров, работающих с копиями одних и тех же баз данных, образует набор реплик. Каждый экземпляр в наборе реплик имеет роль: мастер или реплика.

Реплика получает все обновления от мастера, непрерывно извлекая и применяя его Журнал упреждающей записи (write-ahead log, WAL). Каждая запись в WAL является отдельным запросом на изменение данных Tarantool, таким как INSERT, UPDATE или DELETE, и ей присваивается монотонно возрастающий порядковый номер журнала (LSN). По существу, репликация Tarantool является строчной: каждый запрос на изменение данных полностью детерминирован и воздействует на один кортеж. Однако, в отличие от классического строчного журнала, который содержит полные копии измененных строк, WAL Tarantool содержит копии запросов. Например, для запросов UPDATE Tarantool сохраняет только первичный ключ строки и операции обновления для экономии места.

Ниже описаны особенности добавления различных типов информации в WAL:

  • Вызовы хранимых программ не записываются в WAL. Вместо этого в WAL записываются записи о фактических запросах на изменение данных, выполняемых кодом Lua. Это гарантирует, что возможная недетерминированность Lua не приведет к рассинхронизации репликации.
  • Операции определения данных над временными спейсами (созданными с temporary = true), такие как создание/удаление, добавление индексов и очистка, записываются в WAL, поскольку информация о временных спейсах хранится в постоянных системных спейсах, таких как box.space._space.
  • Операции изменения данных над временными спейсами не записываются в WAL и не реплицируются.
  • Операции изменения данных над локальными спейсами (созданными с is_local = true) записываются в WAL, но не реплицируются.

Подробнее о включении репликации см. в руководстве Начальная загрузка набора реплик.

Этапы репликации

Для создания корректного начального состояния, к которому можно применять изменения из WAL, каждому экземпляру в наборе реплик требуется начальный набор файлов контрольных точек, таких как файлы .snap для memtx и .run для vinyl. Реплика проходит следующие этапы:

  1. Начальная загрузка (необязательный этап)

    При первичной начальной загрузке всего набора реплик не существует мастера, который мог бы предоставить начальную контрольную точку. В таком случае реплики подключаются друг к другу и выбирают мастера. Мастер создает начальный набор файлов контрольных точек и распространяет их на все остальные реплики. Это называется автоматической начальной загрузкой набора реплик.

  2. Присоединение

    На этом этапе реплика загружает начальное состояние от мастера. Мастер регистрирует эту реплику в спейсе box.space._cluster. Если присоединение завершается некритической ошибкой, например, ER_READONLY, ER_ACCESS_DENIED или проблемой сети, экземпляр пытается найти нового мастера для присоединения.

  3. Слежение

    На этом этапе реплика извлекает и применяет обновления из WAL мастера.

Для мониторинга статуса репликации можно использовать свойство box.info.replication[n].upstream.status.

UUID набора реплик и экземпляра

Каждый набор реплик идентифицируется глобально уникальным идентификатором, называемым UUID набора реплик. Этот идентификатор создается мастером, который формирует самую первую контрольную точку, и является частью файла контрольной точки. Он хранится в системном спейсе box.space._schema, например:

tarantool> box.space._schema:select{'cluster'}---- - ['cluster', '6308acb9-9788-42fa-8101-2e0cb9d3c9a0']...

Кроме того, каждому экземпляру в наборе реплик при присоединении присваивается собственный UUID. Он называется UUID экземпляра и является глобально уникальным идентификатором. UUID экземпляра проверяется, чтобы гарантировать, что экземпляры не присоединяются к другому набору реплик, например, из-за ошибки конфигурации. Уникальный идентификатор экземпляра также необходим для того, чтобы строки, исходящие от разных мастеров, применялись только один раз – то есть для реализации многомастерной репликации. Именно поэтому каждая строка в WAL, помимо порядкового номера журнала, хранит идентификатор экземпляра, на котором она была создана. Но использование UUID в качестве такого идентификатора заняло бы слишком много места в WAL, поэтому при присоединении экземпляра к набору реплик ему присваивается более короткое целочисленное значение. Это значение затем используется для ссылок на экземпляр в WAL. Оно называется ID экземпляра. Все идентификаторы хранятся в системном спейсе box.space._cluster, например:

tarantool> box.space._cluster:select{}---- - [1, '88580b5c-4474-43ab-bd2b-2409a9af80d2']...

Здесь ID экземпляра – 1 (уникальный номер в пределах набора реплик), а UUID экземпляра – 88580b5c-4474-43ab-bd2b-2409a9af80d2 (глобально уникальный).

Использование идентификаторов экземпляра также полезно для отслеживания состояния всего набора реплик. Например, свойство box.info.vclock описывает состояние репликации в отношении каждого подключенного узла.

tarantool> box.info.vclock---- {1: 827, 2: 584}...

Здесь значение vclock содержит порядковые номера журнала (827 и 584) для экземпляров с ID экземпляра 1 и 2.

При необходимости можно явно задать значения UUID экземпляра и набора реплик вместо автоматической генерации. Подробнее см. в описании конфигурационного параметра replicaset_uuid.

Роли в репликации: мастер и реплика

Конфигурационный параметр read_only определяет роль в репликации (мастер или реплика). Рекомендуемая роль для всех экземпляров в наборе реплик, кроме одного – read_only (реплика).

В конфигурации мастер-реплика каждое изменение, сделанное на мастере, будет видно на репликах, но не наоборот.

image

Простой набор реплик с двумя экземплярами, один из которых является мастером и расположен на одной машине, а другой является репликой и расположен на другой машине, дает два преимущества:

  • Переключение при отказе, поскольку при отказе мастера реплика может взять его функции на себя, и
  • Балансировка нагрузки, поскольку клиенты могут подключаться как к мастеру, так и к реплике для запросов на чтение.

В конфигурации мастер-мастер (которая также называется "многомастерной") каждое изменение на любом экземпляре будет также видно на другом.

image

Переключение при отказе в таком случае также будет преимуществом, а балансировка нагрузки улучшится, поскольку любой экземпляр может обрабатывать запросы и на чтение, и на запись. В то же время, при многомастерной конфигурации необходимо понимать гарантии репликации, которые обеспечивает асинхронный протокол, реализованный в Tarantool.

Многомастерная репликация Tarantool гарантирует, что каждое изменение на каждом мастере передается на все экземпляры и применяется только один раз. Изменения с одного экземпляра применяются в том же порядке, что и на исходном экземпляре. Однако изменения с разных экземпляров могут смешиваться и применяться в различном порядке на разных экземплярах. В определенных случаях это может привести к рассинхронизации. ß Например, если предположить, что в базу только добавляются данные (т.е. она содержит только вставки), многомастерная конфигурация безопасна. Если данные также удаляются, но не критично, что удаление происходит в том же порядке на разных репликах (например, DELETE используется для удаления устаревших данных), то конфигурация мастер-мастер также безопасна.

Однако операции UPDATE могут легко привести к рассинхронизации. Например, присваивание и инкремент не являются коммутативными и могут дать разные результаты при применении в разном порядке на разных экземплярах.

В более общем случае безопасно использовать репликацию мастер-мастер в Tarantool, только если все изменения в базе данных являются коммутативными: конечный результат не зависит от порядка, в котором применяются изменения. Дополнительную информацию о бесконфликтных типах реплицируемых данных можно получить здесь.

Топологии репликации: каскадная, кольцевая и полносвязная ячеистая

Топология репликации задается конфигурационным параметром replication. Рекомендуемая топология – полносвязная ячеистая топология, поскольку она упрощает возможное переключение при отказе.

Некоторые СУБД предлагают топологии каскадной репликации: создание реплики на реплике. Tarantool не рекомендует такую конфигурацию.

image

Недостаток каскадного набора реплик заключается в том, что некоторые экземпляры не подключаются к другим экземплярам, поэтому не могут получать от них изменения. Одно важное изменение, которое следует передавать на все экземпляры в наборе реплик – запись в системный спейс box.space._cluster с UUID набора реплик. Не зная UUID набора реплик, мастер отклоняет подключения от таких экземпляров при изменении топологии репликации. Вот как это может произойти:

image

Имеется цепочка из трех экземпляров. Экземпляр №1 содержит записи об экземплярах №1 и №2 в своем спейсе _cluster. Экземпляры №2 и №3 содержат записи об экземплярах №1, №2 и №3 в своих спейсах _cluster.

image

Теперь экземпляр №2 неисправен. Экземпляр №3 пытается подключиться к экземпляру №1 как к новому мастеру, но мастер отклоняет подключение, поскольку у него нет записи, например, для №3.

Тем не менее, кольцевая топология поддерживается:

image

Поэтому если необходима каскадная топология, можно первоначально создать кольцо, чтобы все экземпляры знали UUID друг друга, а затем разъединить цепочку в необходимом месте.

Как бы то ни было, для репликации мастер-мастер рекомендуется полносвязная ячеистая топология:

image

В таком случае можно решить, где расположить экземпляры ячейки – в том же центре обработки данных или разместить в нескольких центрах. Tarantool будет автоматически обеспечивать, что каждая строка применяется однократно на каждом экземпляре. Чтобы удалить неисправный экземпляр из ячейки, достаточно изменить конфигурационный параметр replication.

Таким образом можно обеспечить доступность всего кластера в случае локального отказа, например отказа одного экземпляра в одном центре обработки данных, а также в случае отказа всего центра обработки данных.

Максимальное количество реплик в ячейке – 32.

Статус orphan (одиночный)

Во время выполнения функции box.cfg() экземпляр пытается присоединиться ко всем узлам, перечисленным в box.cfg.replication. Если экземпляру не удается подключиться к требуемому числу узлов (см. параметр bootstrap_strategy), он переходит в статус orphan.