Руководство пользователя / Репликация / Архитектура механизма репликации
Руководство пользователя / Репликация / Архитектура механизма репликации

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

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

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

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

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

Вызовы хранимых процедур не регистрируются в журнале упреждающей записи. Между тем, события по запросам изменения фактических данных, которые выполняют Lua-скрипты, регистрируются в журнале. Таким образом, возможное недетерминированное выполнение Lua гарантированно не приведет к рассинхронизации.

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

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

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

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

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

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

Кроме того, каждому экземпляру в наборе реплик присваивается свой UUID, когда он включается в набор реплик. Такой глобально-уникальный идентификатор называется UUID экземпляра*. Этот UUID используется для того, чтобы экземпляры не подключались к различным наборам реплик, например, из-за ошибки конфигурации. Уникальный идентификатор экземпляра также необходим для однократного применения строк от разных мастеров, то есть при многомастерной репликации. Вот почему каждая строка в журнале упреждающей записи, помимо номер записи в журнале, хранит идентификатор экземпляра, где запись была создана. Но использование UUID в качестве такого идентификатора заняло бы слишком много места в журнале упреждающей записи, поэтому экземпляру присваивается целое число при включении в набор реплик. Это число, которое называется 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) для экземпляров с короткими идентификаторами 1 и 2.

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

Настройка репликации

Чтобы включить репликацию, необходимо указать два параметра в запросе box.cfg{}:

  • параметр replication, который определяет источники репликации, и
  • параметр read_only со значением true для реплики и false для мастера.

Both these parameters are «dynamic». This allows a replica to become a master and vice versa on the fly with the help of a box.cfg{} request.

Далее подробно рассмотрим пример настройки набора реплик.

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

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

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

../../../_images/mr-1m-2r-oneway.svg

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

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

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

../../../_images/mm-3m-mesh.svg

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

Многомастерная репликация Tarantool’а гарантирует, что каждое изменение на каждом мастере передается на все экземпляры и применяется только один раз. Изменения с одного экземпляра применяются в том же порядке, что и на исходном экземпляре. Однако изменения с разных экземпляров могут смешиваться и применяться в различном порядке на разных экземплярах. В определенных случаях это может привести к рассинхронизации.

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

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

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

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

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

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

../../../_images/no-cascade.svg

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

../../../_images/cascade-problem-1.svg

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

../../../_images/cascade-problem-2.svg

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

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

../../../_images/cascade-to-ring.svg

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

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

../../../_images/mm-3m-mesh.svg

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

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

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