Дизайн Kafka, реплицированные журналы: кворумы, ISR и машины состояний

По своей сути раздел Kafka - это реплицированный журнал. Реплицированный журнал - один из самых основных примитивов в распределенных системах данных, и существует множество подходов к его реализации. Реплицированный журнал может использоваться другими системами в качестве примитива для реализации других распределенных систем в стиле машины состояний.

Реплицированный журнал моделирует процесс достижения консенсуса в порядке ряда значений (как правило, записи журнала нумеруются 0, 1, 2, ...). Есть много способов реализовать это, но самый простой и быстрый - с лидером, который выбирает порядок значений, предоставляемых ему. Пока лидер остается в живых, всем последователям нужно только копировать значения и порядок, которые выбирает лидер.

Конечно, если бы лидеры не падали, нам не понадобились бы последователи! Когда лидер действительно умирает, нам нужно выбрать нового лидера из числа последователей. Но сами подписчики могут отставать или терпеть неудачу, поэтому мы должны убедиться, что выбрали актуального подписчика. Основная гарантия, которую должен обеспечить алгоритм репликации журнала, заключается в том, что если мы сообщаем клиенту, что сообщение зафиксировано, а лидер выходит из строя, новый лидер, которого мы выбираем, также должен иметь это сообщение. Это дает компромисс: если лидер ждет, пока больше последователей подтвердят сообщение, прежде чем объявить его зафиксированным, тогда будет больше потенциально избираемых лидеров.

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

Обычным подходом к этому компромиссу является использование большинства голосов как для принятия решения, так и для выбора лидера. Это не то, что делает Kafka, но давайте все равно исследуем это, чтобы понять компромиссы. Допустим, у нас есть 2f+1 реплик. Если f+1 реплики должны получить сообщение до того, как коммит будет объявлен лидером, и если мы выберем нового лидера, выбрав последователя с наиболее полным журналом, по крайней мере, из f+1 реплик, то с не более чем f отказов, лидер гарантированно получит все зафиксированные сообщения. Это связано с тем, что среди любых реплик f+1 должна быть хотя бы одна реплика, содержащая все зафиксированные сообщения. Журнал этой реплики будет наиболее полным и, следовательно, будет выбран в качестве нового лидера. Остается еще много деталей, которые должен обрабатывать каждый алгоритм (например, точное определение того, что делает журнал более полным, обеспечение согласованности журнала во время отказа лидера или изменение набора серверов в наборе реплик), но мы пока их проигнорируем.

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

В этом семействе есть множество разнообразных алгоритмов, включая ZooKeeper's Zab, Raft и Viewstamped Replication.

Обратной стороной большинства голосов является то, что не нужно много неудач, чтобы остаться без избранных лидеров. Чтобы выдержать один отказ, требуется три копии данных, а для выдерживания двух отказов требуется пять копий данных. Наличие достаточной избыточности, чтобы выдержать единичный сбой, недостаточно для практической системы, но выполнение каждой записи пять раз, с 5-кратным требованием к дисковому пространству и 1/5 пропускной способностью, не очень практично для проблем с данными большого объема. Вероятно, поэтому алгоритмы кворума чаще появляются для конфигурации общего кластера, такой как ZooKeeper, но менее распространены для первичного хранилища данных. Например, в HDFS функция высокой доступности namenode построена на журнале, основанном на большинстве голосов, но этот более дорогой подход не используется для самих данных.

Kafka использует несколько иной подход к выбору набора кворума. Вместо большинства голосов Kafka динамически поддерживает набор синхронизированных реплик (ISR, in-sync replicas), которые догоняют лидера. Только члены этого набора могут быть избраны лидером. Запись в раздел Kafka не считается зафиксированной, пока все синхронизированные реплики не получат запись. Этот набор ISR сохраняется в ZooKeeper при каждом изменении. Благодаря этому любая реплика в ISR может быть избрана лидером. Это важный фактор для модели использования Kafka, в которой много разделов и важно обеспечить баланс лидерства. С этой моделью ISR и репликами f+1 тема Kafka может терпеть сбои f без потери зафиксированных сообщений.

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

Еще одно важное отличие дизайна заключается в том, что Kafka не требует восстановления поврежденных узлов со всеми их данными без изменений. Алгоритмы репликации в этом пространстве нередко зависят от существования «стабильного хранилища», которое не может быть потеряно в любом сценарии восстановления после сбоя без потенциальных нарушений согласованности. Это предположение связано с двумя основными проблемами. Во-первых, дисковые ошибки являются наиболее распространенной проблемой, наблюдаемой в реальной работе систем постоянного хранения данных, и они часто не оставляют данные нетронутыми. Во-вторых, даже если бы это не было проблемой, Kafka не планирует требовать использования fsync при каждой записи для гарантий согласованности, поскольку это может снизить производительность на два-три порядка. Протокол Kafka, позволяющий реплике повторно присоединиться к ISR, гарантирует, что перед повторным присоединением она должна снова полностью повторно синхронизироваться, даже если она потеряла не записанные данные в результате сбоя.


Читайте также:

Комментарии

Популярные сообщения из этого блога

Язык поисковых запросов в Graylog

Хэш-таблица: разрешение коллизий

Нормальные формы, пример нормализации в базе данных