
Введение
Выбор подходящей системы управления базами данных (СУБД) — важнейшая задача при проектировании программных систем. Разработчики и архитекторы учитывают множество факторов: модель данных (реляционная или NoSQL), поддержку транзакций, масштабируемость, требования к согласованности и многого другое. Одним из ключевых архитектурных аспектов, влияющих на эффективность и надежность системы, является модель репликации данных. Репликация означает поддержание копий одних и тех же данных на нескольких узлах (серверах), соединённых по сети.
Зачем это нужно? Репликация позволяет: во-первых, держать данные ближе к пользователям (уменьшая задержку при запросах); во-вторых, продолжать работу системы даже при сбое отдельных узлов (повышая доступность); в-третьих, масштабировать систему, увеличивая число узлов для обслуживания запросов на чтение (повышая пропускную способность).
Однако реализация репликации сопряжена с серьёзными архитектурными компромиссами. Согласно теореме CAP, в распределённой системе невозможно одновременно гарантировать все три свойства: консистентность данных, доступность сервиса и устойчивость к разделению сети. При возникновении сетевых сбоев (разбиении на изолированные сегменты) системе приходится жертвовать либо мгновенной согласованностью данных, либо доступностью части узлов. Поэтому разные СУБД делают разные выборы в этих компромиссах. Архитектурная модель репликации, лежащая в основе СУБД, определяет, как база данных достигает (или не достигает) консистентности, доступности и отказоустойчивости. Понимание этих различий крайне важно для архитекторов и разработчиков: зная поведение репликации, вы сможете выбрать такую СУБД, которая лучше соответствует требованиям вашего проекта по масштабу, геораспределенности, допустимой задержке и устойчивости к сбоям.
В данной статье мы подробно рассмотрим три основные модели репликации в современных распределённых базах данных, их принципы работы, сильные и слабые стороны, типичные случаи использования и примеры СУБД. Также мы проанализируем, как каждая модель влияет на согласованность, доступность и отказоустойчивость системы (термины CAP-теоремы) и как сказывается на масштабируемости, сложности разработки, latency (задержках) и доступности сервиса при сбоях.
Репликация данных может осуществляться тремя популярными способами
С одним ведущим узлом (Single‑leader replication) — классическая модель «ведущий‑ведомый», при которой все записи проходят через один главный узел.
С множеством ведущих узлов (Multi‑leader replication) — все узлы равноправно принимают изменения, реплицируя их друг другу.
Без ведущих узлов (Leaderless replication) — система без явного мастера: клиентские операции распределяются между репликами с помощью квормумов и других механизмов.
Перейдём к детальному рассмотрению каждой модели.
Репликация с одним ведущим узлом (Single-leader replication)

Принцип работы: В модели с одним лидером один из узлов кластера назначается главным (leader, мастер, primary). Все операции записи (изменения данных) от клиентов направляются исключительно на этот ведущий узел. Лидер выполняет запись у себя локально и фиксирует изменения (например, в журнал транзакций), после чего распространяет поток изменений на остальные узлы, называемые последователями или репликами (follower, slave, secondary). Реплики получают от лидера записи изменений (обычно в том же порядке, в каком они применены на лидере) и применяют их к своим локальным копиям базы данных. Таким образом, все данные, изменённые через лидера, в итоге копируются на каждого последователя. Чтения могут выполняться с любого узла: как с самого лидера, так и с его реплик. Однако важно понимать, что если читать с отстающих реплик, данные могут быть не полностью актуальными (запрос может увидеть устаревшее состояние, не содержащее самых недавних транзакций). По этой причине многие системы по умолчанию направляют чтения также на лидер, либо обеспечивают специальный режим “read your own writes” (чтение своих записей), при котором после записи клиент читает либо с лидера, либо с такой реплики, которая уже получила свежие данные.
Примеры СУБД: Модель с одним ведущим является наиболее распространённой и поддерживается из коробки во многих популярных СУБД. Практически все классические реляционные СУБД используют репликацию мастер-слейв: например, PostgreSQL (начиная с версии 9.0) и MySQL позволяют настроить один основной сервер и один или несколько реплицирующихся слейвов. Аналогичный подход применяется в Oracle (технология Data Guard) и Microsoft SQL Server (группы доступности AlwaysOn). Среди NoSQL систем эту модель используют MongoDB (репликация в реплика-сетах через один primary сервер) и Riak (в режиме single-master) и др. Например, в документации MongoDB прямо указано, что первичный узел (primary) единственный принимает все операции записи, а вторичные узлы асинхронно реплицируют журнал операций (oplog) первичного и применяют эти операции к своим данным. Такой подход также называют активный/пассивный (active-passive) или master-slave репликацией.
Плюсы модели с одним лидером:
Простая и последовательная модель данных. В любой момент времени все подтверждённые записи проходили через один узел, что упрощает обеспечение согласованности. Нет ситуаций одновременного конфликта изменений – параллельные транзакции на лидере могут сериализоваться с помощью механизмов блокировок/ACID, а на репликах конфликтов не возникает, так как они просто воспроизводят порядок операций лидера. Это упрощает программирование и исключает необходимость сложного разрешения конфликтов данных
Высокая производительность чтения. За счёт наличия нескольких копий данных можно масштабировать нагрузку на чтение: добавить больше реплик и распределять запросы чтения между ними. Лидер разгружается, так как часть селектов обслуживается последователями. В итоге общий throughput по чтению масштабируется практически линейно (ограничен только количеством и производительностью реплик).
Относительно простая реализация и понимание. Алгоритм репликации с мастером хорошо изучен десятилетиями и реализован во множестве СУБД. Администрирование такой репликации сравнительно несложное: добавление новой реплики происходит через инициализацию копии данных и подключение к лидеру для получения изменений. Модель понятна разработчикам приложения – фактически, система выглядит как одна база данных для записей, плюс дополнительные узлы для ускорения чтения.
Сильная консистентность (при правильной настройке чтений). Если приложение читает данные с лидера (или с реплики, но с задержкой после своей записи), то оно видит самые актуальные, консистентные данные. В терминах CAP эта модель может обеспечивать строгую согласованность (Consistency) — по крайней мере для клиентов, читающих из лидера. Репликация часто настраивается асинхронно, поэтому при нормальной работе лидер всегда имеет самые новые данные, а лаг реплик не влияет на видимость данных на самом лидере.
Минусы и ограничения:
Единая точка отказа для записи. Главный недостаток модели с одним ведущим — наличие единственного узла, через который проходят все операции записи. Если лидер становится недоступен (например, из-за сбоя или сетевой проблемы), вся система теряет способность принимать изменения данных. До переключения на нового лидера (failover) клиенты не могут выполнять записывающие транзакции. Хотя стандартные реализации предусматривают автоматический выбор нового лидера при отказе старого, этот процесс занимает время, в течение которого система уязвима. Таким образом, для поддержания доступности при отказе требуется настроенный механизм фейловера, который сам по себе усложняет систему.
Ограниченная масштабируемость записи. Пропускная способность операций записи ограничена производительностью единственного лидера. Даже если у вас десятки реплик, быстрее вставлять или обновлять данные не получится — все изменения должны последовательно обработаться одним узлом. Масштабировать запись горизонтально в этой модели невозможно, кроме как через шардинг данных (разбиение по нескольким мастер-серверам, каждый из которых будет лидером для своего фрагмента данных). Шардинг усложняет архитектуру и выходит за рамки самой репликации.
Репликационное запаздывание и устаревшие данные на репликах. Так как репликация обычно происходит асинхронно (ведущий не ждёт подтверждения применения операций на всех последователях при транзакции), существует лаг репликации — задержка между фиксацией данных на лидере и появлением этих данных на копиях. В течение этого интервала чтения с реплик возвращают устаревшую информацию. Это может приводить к аномалиям вроде нарушения Read-after-write консистентности (когда сразу после записи клиент не видит свои данные при чтении с другой реплики) и не монотонным чтениям (когда прочитав более новую версию данных, позже можно увидеть более старую, обратившись к другой реплике). Для борьбы с этим часто применяют: принудительное чтение «своих» записей с лидера, привязку сессии пользователя к конкретной реплике, ожидание репликации перед чтением и т.п. Эти меры усложняют разработку, но необходимы для корректности в асинхронной репликации.
Риск потери данных при отказе лидера. Если репликация асинхронная, то сбой лидера может привести к потере последних подтверждённых пользователю транзакций, которые ещё не успели примениться на репликах. Представим: лидер зафиксировал запись и вернул клиенту успех, но через миллисекунды вышел из строя, не передав изменения последователям. Тогда новый лидер, выбранный из бывших реплик, не будет содержать этих изменений – данные потеряны. Именно поэтому многие СУБД позволяют настраивать синхронную репликацию (лидер ждёт подтверждения хотя бы от одного или нескольких реплик на каждую транзакцию). Синхронный режим повышает надёжность хранения (данные гарантированно реплицированы хотя бы на двух узлах при коммите), но в ущерб производительности: время подтверждения транзакции увеличивается на задержку сети между узлами и снижается скорость работы. Кроме того, синхронность снижает доступность системы записи: если требуемое число реплик недоступно (например, одна реплика отстала или потеряна связь), лидер не сможет подтверждать новые транзакции, что фактически приостанавливает работу. Таким образом, администратору приходится выбирать между быстротой/доступностью (async) и надёжностью/консистентностью (sync). Многие развёртывания по умолчанию используют асинхронную репликацию, осознавая риск потери части данных при катастрофическом сбое лидера
Типичные случаи использования: Модель с одним ведущим хорошо подходит для систем, где требуется строгая консистентность данных и преобладают операции чтения. Большинство традиционных приложений (финансовые системы, учёт, заказные системы, CMS, и т.д.) могут эффективно работать с одной точкой записи, полагаясь на механизмы СУБД для сериализации транзакций. Репликация в таком случае используется главным образом для повышения доступности (наличие горячих standby-реплик на случай сбоя) и для распределения нагрузки на чтение (реплики обслуживают часть запросов). Географически распределённые системы также часто начинают с модели single-leader, размещая базу данных в одном датацентре и используя реплики для удалённых регионов только как read-only кеш. Например, можно держать основной лидер в одной локации, а в других регионах развернуть только реплики для быстрых локальных чтений (при этом запись всё равно идёт в главный центр). Это обеспечивает целостность данных (т.к. конфликтов нет), ценой увеличенной задержки записи из удалённых регионов. Таким образом, single-leader репликация оправдана, когда приложение может терпеть небольшой даунтайм при переключении лидера и потенциальную eventual consistency при чтениях с слейвов, но выигрывает от упрощённой модели согласованности.
Согласованность, доступность и отказоустойчивость (CAP): С точки зрения CAP-теоремы система с одним лидером обычно стремится обеспечить консистентность (Consistency) и устойчивость к разделению (Partition Tolerance) в ущерб полной доступности при сетевых сбоях. Если происходит разделение сети между лидером и остальными узлами, или клиент теряет связь с лидером, то запись останавливается для сохранения согласованности – другими словами, система выбирает быть CP (Consistent + Partition-tolerant) и жертвует доступностью части операций. Такая СУБД не будет принимать противоречивые записи в разрезанных сегментах, а предпочтёт дождаться восстановления связи или выбора нового лидера. В нормальном режиме single-leader может обеспечить линейную согласованность (все клиенты видят изменения в одном порядке – порядке лидера). Доступность чтения при этом высокая: даже если некоторые реплики недоступны, лидер продолжает работать и может обслуживать запросы; если же недоступен сам лидер, система проводит фейловер и восстанавливает работу (обычно в течение секунд или минут, в зависимости от настройки). Отказоустойчивость системы достигается за счёт реплик: при падении лидера данные не теряются (если репликация была хотя бы частично успела примениться на других узлах), и один из оставшихся узлов может быть назначен новым лидером. Но до момента переключения сервис записи недоступен, что снижает общую availability. Таким образом, single-leader репликация даёт сильную согласованность и относительно простую модель отказоустойчивости (failover), но не обеспечивает полной непрерывности записей при любых сбоях.
Влияние на масштабируемость, сложность разработки, задержки и доступность при сбоях:
Масштабируемость: чтение масштабируется горизонтально очень хорошо (можно добавлять много реплик для распределения нагрузки), однако масштабирование операций записи ограничено одним узлом. Если ваше приложение начинает упираться в пределы производительности лидера, придётся либо масштабировать его «вверх» (vertical scaling – более мощное железо), либо переходить к шардингу (разделению данных между несколькими независимыми кластерами с отдельными лидерами). Это усложняет архитектуру.
Сложность разработки: модель с одним лидером относительно проста для разработчиков. Вам не нужно думать о конфликтных обновлениях данных — система гарантирует порядок выполнения транзакций. Основная сложность — правильно учитывать лаг репликации при чтении. Например, если пользователь сразу после записи видит устаревшие данные, это может нарушить бизнес-логику. Разработчикам приходится внедрять механизмы вроде мониторинга лагов или использовать консистентные чтения (например, MongoDB позволяет задать readConcern: "majority", чтобы читать только подтверждённые большинством узлов данные). Но в целом когнитивная нагрузка ниже, чем в системах с несколькими мастерами.
Задержки (latency): задержка на запись определяется сетью между клиентом и лидером и скоростью фиксации на лидере. Если клиенты распределены глобально, то удалённые от лидера клиенты будут испытывать бóльшую задержку из-за сетевых кругов до датацентра лидера. Чтения могут быть очень быстрыми, если выполняются из ближайшей реплики. Но если требуется strong consistency на чтение, часто приходится читать с лидера, жертвуя скоростью ради актуальности. Синхронная репликация увеличивает latency записи, так как ждёт подтверждения от реплик. Асинхронная репликация не влияет на latency записи (лидер сразу отвечает), но приводит к упомянутому лагу для чтений.
Доступность при сбоях: при падении ведущего узла система переживает короткую недоступность для операций записи до завершения фейловера. Хорошо настроенный кластер с автоматическим выбором нового лидера может минимизировать простой (несколько секунд). Чтения при этом могут временно обслуживаться с устаревших реплик (если это допустимо приложением). После восстановления кворума данных (например, старый лидер вернулся или был устранён) кластер продолжает работу с новым лидером. В случае серьёзного разделения сети (split brain) возможны более тяжёлые сбои — например, два узла могут считать себя лидерами, что требует внешнего механизма (quorum, fencing) для предотвращения одновременной работы двух мастеров. В целом, single-leader система обеспечивает высокую доступность чтения и хорошую устойчивость к единичным сбоям, но не к одновременной недоступности нескольких узлов или сети.
Репликация с несколькими ведущими узлами (Multi-leader replication)

Принцип работы: В модели с множеством лидеров (она же master-master, active/active) отсутствует выделенный единый мастер – каждый узел кластера может принимать операции записи.
Такие узлы называют лидерами или мастерами, и каждый из них действует независимо для локальных клиентов. Разумеется, изменения данных, принятые одним узлом, должны быть доставлены на остальные, чтобы в итоге все копии данных синхронизировались. Поэтому реализуется механизм обмена записями между всеми мастерами: каждый лидер пересылает поток своих локальных операций другим лидерам (и, возможно, дополнительным репликам-последователям, если те настроены для масштабирования чтения). Происходит нечто вроде двунаправленной асинхронной репликации: узлы обмениваются логами изменений друг с другом. В результате каждый участник рано или поздно применит изменения, совершённые на любом из других лидеров. Можно представить себе круговую топологию (когда обновления передаются по кольцу между узлами) или всем-ко-всем (каждый отправляет каждому). Так или иначе, система стремится к тому, чтобы в конечном итоге все узлы-участники получили все изменения.
Консистентность и конфликты: Главная сложность multi-leader репликации состоит в том, что одни и те же данные могут одновременно изменяться на разных узлах. Поскольку репликация происходит асинхронно, ситуации, когда две транзакции в разных центрах противоречат друг другу, обнаруживаются после факта, при обмене логами. В отличие от single-leader системы, здесь нет центра, который бы наложил тотальный порядок на все операции — изменения происходят параллельно. Следовательно, возникают конфликты записи, которые надо как-то разрешать. Например, если два пользователя одновременно отредактировали один и тот же документ в разных географических узлах, каждая копия документа сначала обновится по-своему, а при взаимной репликации обнаружится расхождение версий. Варианты решения проблемы: откат (undo) одной из операций, слияние изменений, выбор одного из апдейтов как приоритетного и т.д. В базе с одним лидером подобное не случается — там вторая параллельная транзакция либо ждёт, либо завершается с ошибкой, но не приводит к дивергенции данных. В multi-leader же конфликт обнаруживается постфактум, когда обе операции уже выполнены локально. Если откладывать подтверждение транзакции до глобального соглашения, то смысл нескольких лидеров пропадает (это сведётся к распределённому консенсусу, как в Apache ZooKeeper или NewSQL СУБД). Поэтому большинство реализаций multi-master полагаются на асинхронное обнаружение конфликтов и последующее разрешение. Это может происходить автоматически (по алгоритму) или требовать участия приложения/пользователя.
Стратегии разрешения конфликтов:
Принцип «последний победил» (Last Write Wins). Самый простой подход – принять за окончательное значение ту запись, которая имеет более позднюю временную метку (предполагается, что «последняя по времени» операция должна остаться). Например, если атрибут записи изменился в двух местах, можно сравнить временные метки изменений и оставить значение с большей меткой. Этот метод легко реализуется (например, в Amazon DynamoDB Globаl Tables при конфликте по умолчанию используется Last Write Wins), но он может приводить к потере данных: изменение, которое «проиграло», просто перезаписывается и не сохраняется нигде, даже если оно было важным.
Версионные векторы (vector clocks) и слияние версий. Более продвинутый подход – хранить несколько версий объекта, помеченные специальными версиями (векторами версий), которые позволяют обнаруживать параллельные изменения. Если система детектирует, что два апдейта произошли независимо, она может сохранить обе версии (так называемые siblings) и предоставить их приложению для явного разрешения. Либо же автоматизировать слияние: например, если это текст, пытаться слить изменения, как это делает система контроля версий (git) или как реализовано в CouchDB для документов. Векторные часы применялись в оригинальной системе Amazon Dynamo, позволяя выявлять, произошли ли два изменения в причинно-упорядоченной последовательности или независимо. Хранение нескольких версий увеличивает сложность — на чтении нужно возвращать все альтернативные версии или автоматически склеивать их по какому-то правилу.
CRDT (Конфликтно-устойчивые реплицированные типы данных). Это класс специальных структур данных, разработанных для автоматического слияния одновременно произведённых операций без потерь. Примеры CRDT: счетчики, множества, списки, позволяющие объединять параллельные обновления так, чтобы итоговое состояние было консистентным и включало вклады всех участников. CRDT используются, например, в системах совместного редактирования (похожая задача – слияние правок нескольких пользователей). Применение CRDT может устранить необходимость в явном разрешении конфликтов приложением, но ограничено определёнными типами операций
Разделение ответственности (отмена конфликтов на уровне дизайна). Часто лучший способ справиться с конфликтами – предотвратить их. Если возможно, стоит спроектировать систему так, чтобы одни и те же данные не редактировались одновременно в разных местах. Например, географическое разделение по владельцу данных: можно закрепить каждую запись или пользователя за конкретным датацентром (лидером). Тогда все операции над данным объектом идут через один выбранный лидер, избегая конфликтов с другими. Такой подход требует маршрутизации запросов на «правильный» узел и не всегда универсален, но во многих сценариях (разделение пользователей по регионам, разбиение ключей) позволяет использовать multi-leader репликацию практически без конфликтных ситуаций. В некоторых СУБД существуют настройки, принуждающие определённые таблицы или разделы данных быть single-master даже внутри multi-master кластера.
Следует отметить, что конфликтами ограничиваются не только одновременные правки одного поля записи. Возможны более сложные нарушения консистентности: авто-инкрементируемые идентификаторы, уникальные ключи, счета и балансы – всё это трудно согласовать в мульти-мастер среде. Например, два узла могли выдать дублирующиеся первичные ключи в разных транзакциях, и при слиянии возникнет коллизия. Поэтому multi-leader решения часто требуют тщательной проработки логики приложения или используют глобальные сервисы для генерации уникальных идентификаторов.
Плюсы модели с несколькими лидерами:
Высокая доступность записи и отсутствие единой точки отказа. Падение любого отдельного узла не останавливает возможность записей в систему – клиенты могут переключиться на другой доступный датацентр/узел и продолжать работу. В случае сетевого разделения (например, разрыв связи между двумя центрами) каждая сторона может автономно принимать обновления от своих клиентов, не блокируя их. Это обеспечивает максимальную availability: система остаётся работоспособной даже при значительных сбоях связи, по крайней мере локально для каждого сегмента. Данный подход ценен для критически важных приложений, требующих непрерывности обслуживания.
Локальные задержки при записи. Если пользователи распределены по разным географическим регионам, multi-leader позволяет им выполнять записи в локальный, ближайший узел, избегая долгих сетевых задержек до единого центрального мастера. Например, глобальное приложение с пользователями в Европе, Азии и Америке может иметь по лидеру в каждом регионе; местные пользователи будут работать со своим узлом с минимальным latency. Это улучшает опыт пользователей и разгружает международные каналы.
Разделение нагрузки на запись. В теории, несколько лидеров могут повышать совокупную пропускную способность системы по записям, поскольку параллельные транзакции распределяются между разными узлами. Нет «узкого места» в виде единственного сервера, который должен последовательно обработать все операции. Например, если один лидер перегружен, часть трафика можно направить на другой. Это даёт определённый уровень горизонтального масштабирования для записей. Нужно оговориться, что выигрыш не линейный: каждый лидер всё равно рано или поздно реплицирует изменения на других, так что общий объём работы системы остаётся тот же. Но параллелизация и распределение по узлам может сгладить пики нагрузки и снизить нагрузку на отдельный экземпляр.
Географическая устойчивость и балансировка. Multi-leader схема естественным образом подходит для multi-datacenter развертываний, где несколько датацентров активно обслуживают запросы (режим active-active). В отличие от классического режима active-passive (когда один центр активен, второй пассивен и ждёт переключения), здесь оба (или более) центра могут одновременно использоваться под нагрузкой, обмениваясь данными. Это лучше утилизирует ресурсы и обеспечивает бесшовный фейловер: если один центр полностью выходит из строя (стихийное бедствие, отключение), остальные просто продолжают работать со своими данными, а пропавший центр при возвращении синхронизируется.
Поддержка офлайн-режима и синхронизация клиентов. Ещё одна ниша применения multi-master – приложения, которые должны работать в автономном режиме и синхронизироваться позже. Классический пример – мобильные или десктопные приложения с локальной базой данных (SQLite, LiteDB и т.п.). Пользователь может внести изменения офлайн, а при подключении устройство реплицирует свои изменения на сервер и получает чужие. В такой схеме устройство выступает как временный лидер для своих данных. СУБД CouchDB, например, реализует репликацию документов между несколькими узлами с возможностью конфликтов, ориентируясь именно на офлайн-сценарии. Это удобно для систем заметок, календарей, контактов, где каждое устройство имеет копию данных и синхронизируется с другими через облако
Минусы и сложности multi-leader репликации:
Сложность обеспечения консистентности. Как подробно описано выше, главный недостаток – необходимость разрешения конфликтов и обеспечение целостности данных в условиях параллельных записей. Разработчикам приходится продумывать обработку конфликтных ситуаций, что значительно усложняет приложение. В некоторых случаях требуются дополнительный код и сервисы для синхронизации (например, merge resolution handlers для слияния записей, запись логов конфликтов, оповещение ответственных систем). Даже при наличии автоматических стратегий (LWW, CRDT) всегда остаётся риск некорректного объединения изменений или потери данных, особенно для сложных взаимосвязанных сущностей.
Eventual consistency – слабая моментальная согласованность. Multi-master системы, как правило, не могут обеспечить строгую согласованность данных в реальном времени. Поскольку изменения распространяются асинхронно, разные узлы в один момент могут содержать различающееся состояние. Клиент, читающий данные в двух разных локациях, может наблюдать две разные версии – до тех пор, пока репликация не синхронизирует всё (то есть в конечном счёте данные сравняются). Поэтому такие системы обычно классифицируют как eventually consistent – гарантия даётся лишь на сходство данных в конечном итоге. В терминах CAP, multi-leader модель жертвует строгой консистентностью ради высокой доступности при разделениях, фактически предоставляя очень слабые гарантии согласованности. Это накладывает ограничения на типы приложений: для некоторых задач (банковские счета, транзакции с деньгами, критичные инвентори-системы) такая семантика неприемлема или требует надстроек (например, централизованного учёта баланса вне самой БД).
Сложность реализации и сопровождения. Двунаправленная репликация сложнее в настройке и отладке. Требуется предотвратить бесконечное циркулирование обновлений (когда изменение ходит по кругу), для этого обычно используют идентификаторы источника изменений или временные метки. Необходимо следить за тем, что все узлы находятся в консистентном состоянии или выявлять рассинхронизацию. Диагностика проблем (например, «почему у нас расхождение данных между узлом А и В?») превращается в нетривиальную задачу. Многие СУБД исторически не поддерживали multi-master из-за этих сложностей. Существующие решения зачастую являются внешними надстройками или требуют определённых шаблонов развертывания (например, MySQL с Galera Cluster предоставляет синхронную мульти-мастер репликацию, но вводит собственные ограничения, а PostgreSQL в стандартной поставке не имеет multi-master, предлагаются сторонние расширения вроде BDR). Всё это ведёт к увеличению операционной сложности для команд DevOps и DBA.
Непредсказуемость задержек при слиянии. Пока система исправляет конфликты, пользователи могут видеть нестыковки. Например, при объединении версий документа может потребоваться время, в течение которого часть пользователей видит старую версию, часть новую, или документ помечен как конфликтный. Это негативно сказывается на UX и требует дополнительной работы по информированию пользователей или блокировке объектов на время слияния.
Оверхед репликации. В multi-leader схемах общий сетевой трафик репликации может быть выше, ведь каждое изменение приходится пересылать каждому лидеру. В кластере из N узлов каждая транзакция в худшем случае распространяется N-1 пересылкой (на каждый другой узел). В single-leader обычно тоже N-1 (лидер -> каждый фолловер), но там один поток исходящий, а здесь каждый генерирует поток. При всем-ко-всем подключении количество связей растёт квадратично с числом узлов, что плохо масштабируется. Обычно это решают организацией репликации по кольцу или через выделенный координирующий узел, но тогда растёт сложность и задержка распространения.
Типичные случаи использования: Multi-leader репликация применяется относительно редко и лишь при обоснованной необходимости. Типичные сценарии: географически распределённые активные датацентры (Active-Active DC). Компании, не желающие иметь пассивный резерв, настраивают несколько ЦОДов на одновременную работу. Например, основные активные базы могут быть в Европе и США, обслуживая каждый «свои» регионы, а изменения реплицируются между ними. Это позволяет пережить падение одного из ЦОД без простоя, а также улучшает отклик для локальных пользователей. Однако, как отмечалось, такой дизайн сложен. Multi-master внутри одного датацентра обычно не имеет смысла, а оправдан в меж-датацентровой конфигурации.
Другой сценарий — офлайн-режим и синхронизация устройств, рассмотренный ранее (CouchDB, мобильные приложения). Здесь конфликтные изменения редки или решаются на уровне приложения (например, пользователь при синхронизации выбирает правильную версию данных). Коллаборативные приложения тоже могут рассматриваться как multi-leader среда: например, в Google Docs нет центрального сервера, последовательно применяющего все правки; клиенты и серверы обмениваются операциями редактирования, сливая их с помощью алгоритмов вроде Operational Transformation или CRDT, что по сути является случаем многолидерной репликации для достижения совместной работы. Наконец, multi-leader применяется для интеграции разных систем: например, когда данные из одной базы нужно реплицировать в другую двунаправленно (сценарии миграции, объединения данных разных источников). В таких случаях могут помочь внешние инструменты (Oracle GoldenGate для двухсторонней репликации Oracle, PostgreSQL BDR для двунаправленной репликации Postgres и пр.)
Согласованность, доступность, отказоустойчивость (CAP): Multi-leader системы, как правило, делают упор на доступность (Availability) и устойчивость к разделениям, жертвуя строгой согласованностью. В условиях распределённой сети они допускают, что разные узлы могут принимать противоречивые операции, но не останавливают обслуживание (то есть система остаётся доступной клиентам в каждом разделе). Это означает, что при сетевом разделении каждый сегмент кластера продолжит работать автономно – Partition Tolerance + Availability, как и предсказывает CAP. Цена этого – временная неконсистентность между сегментами (нарушение модели единой базы данных). Формально, такая система предлагает лишь eventual consistency (в конечном итоге данные сольются). Таким образом, multi-master реализации обычно классифицируют как AP-системы в тройке CAP. Однако это общее правило: есть варианты, где при обнаружении разделения вторичный узел блокирует операции, чтобы избежать конфликтов (например, некоторые кластеры стараются остаться в режиме CP, отключая один из центров). Но тогда теряется основное преимущество — доступность. Поэтому при правильном использовании multi-leader архитектуры нужно быть готовым мириться с потенциальной рассинхронизацией данных ради лучшей доступности.
Отказоустойчивость таких систем на уровне узлов выше: выход из строя отдельного сервера мало влияет, т.к. другие продолжают приём записей. Даже потеря целого датацентра не уничтожает данные, если они дублировались на других узлах. Но важно учитывать, что если узел упал до репликации своих транзакций, эти транзакции могут не сохраниться нигде, то есть их изменения потеряются (аналогично асинхронному режиму single-master, только тут это нормальное явление при катастрофе). В целом, multi-leader конфигурация повышает доступность системы (нет единой точки отказа), но снижает гарантию консистентности и усложняет достижение устойчивого согласованного состояния после сбоев.
Влияние на масштабируемость, сложность разработки, задержки и доступность:
Масштабируемость: Multi-leader позволяет распределить нагрузку на запись, но имеет пределы. При небольшом числе узлов (2-3) мы действительно можем получить почти суммарную производительность, близкую к удвоению/утроению одиночного узла (особенно если данные “разделены” по лидерам географически). Но по мере роста числа реплик выигрыши снижаются, а издержки обмена и слияния растут. Как было сказано, каждый лидер всё равно должен применить все операции, пусть и с некоторой задержкой. Следовательно, горизонтально масштабировать бесконечно запись через multi-leader не получится – узкое место просто превратится из CPU лидера в полосу пропускания сети и CPU, тратящийся на синхронизацию. Поэтому кластеры с десятками активных мастеров редки; обычно их 2-5. Для очень высоких нагрузок чаще комбинируют подходы: например, шардирование + single-leader на шард (что по сути даёт много маленьких single-leader систем) или используют leaderless системы.
Сложность разработки: Multi-leader – наиболее сложная для программирования модель. Разработчики должны предусмотреть конфликтные ситуации, понять поведение базы при расхождении данных. В простых случаях СУБД может скрыть это (например, если просто настроить два MySQL в кольцевой репликации и надеяться на лучшее), но рано или поздно возникают аномалии, которые без приложения не решить. Например, тот же конфликт уникального ключа или двойное списание товара со склада. Часто разработчикам приходится имплементировать дополнительный уровень согласования: distributed locks, уникальные генераторы, ручное восстановление консистентности в случае конфликта (например, оповещать ответственного оператора). Всё это усложняет код и повышает риск ошибок. Тестирование распределённых условий (например, падение связи между двумя датацентрами во время нагрузки) – непростая задача. Поэтому multi-master применяется опытными командами и только при оправданной бизнес-необходимости. В противном случае, как шутят, «единственный действительно безопасный multi-master – это тот, где в приложении пишут только в один мастер», то есть фактически возвращаются к single-master.
Задержки: В нормальном режиме задержка локальной записи минимальна – клиент обращается к ближнему узлу, который мгновенно подтверждает транзакцию (никаких удалённых ожиданий). Проблемы начинаются, когда данные, записанные в одном месте, почти сразу нужны в другом. Из-за асинхронности репликации изменения доходят до удалённого узла с некоторой задержкой (это миллисекунды или секунды – зависит от расстояния, нагрузки). Если пользователь из США обновил профиль, а через секунду его друг в Европе пытается его прочитать, высок шанс, что европейский узел ещё не получил новую версию и вернёт старые данные. То есть latency чтения актуальных данных кросс-регионально равна задержке репликации. Эту проблему можно частично обходить: либо направлять чтение таких чувствительных данных к тому же узлу, где была запись (но это не всегда возможно), либо использовать специальные механизмы глобального порядка (что усложняет систему). В целом, multi-master улучшает локальные задержки, но не устраняет межрегиональных задержек распространения данных.
Доступность и поведение при сбоях: Главное достоинство – устойчивость к отказам целого узла или сегмента. Даже серьёзное разделение (split-brain между датацентрами) не парализует систему: каждый сегмент продолжает работать. Однако после восстановления связи вас ждёт фаза схождения данных, потенциально с множеством конфликтов, требующих разрешения. В этот момент, возможно, придётся временно приостановить некоторые операции или переключить приложение в режим только чтения, пока не устранены рассогласования (всё зависит от алгоритмов). Отказы отдельных узлов обрабатываются прозрачно: их изменения либо уже реплицированы, либо будут восстановлены с других копий. Распределённые системы часто реализуют концепцию quorum даже в мульти-мастере: например, можно требовать, чтобы для финального подтверждения транзакции она записалась хотя бы на один другой узел. Это уменьшает вероятность потери данных при одновременном фатальном сбое узла, но замедляет систему и частично нивелирует преимущества. Некоторые системы (например, Microsoft SQL Server в режиме peer-to-peer replication) накладывают ограничения, чтобы уменьшить вероятность конфликтов, но всё равно рекомендуют не активничать одновременно из разных узлов. В итоге, доступность multi-leader при сбоях максимальна, но с точки зрения целостности данные могут потребовать ручной проверки после серьёзных аварий.
Репликация без ведущих узлов (Leaderless replication)

Принцип работы: Модель leaderless (дословно «без лидера») устраняет понятие мастер-узла вовсе. Здесь каждый узел равноправен, и клиентская операция (запись или чтение) может быть обработана совокупностью нескольких узлов. Обычно схема такова: клиент при записи отправляет запрос на несколько реплик (несколько узлов кластера одновременно) и ждёт, что хотя бы определённое количество из них подтвердят успешное выполнение. Аналогично, при чтении клиент опрашивает несколько реплик и берет наиболее актуальный ответ (либо осуществляет слияние/ремонт данных). Такой подход часто реализуется посредством кворумов: задаются числа W (минимум подтверждений для записи) и R (минимум узлов для успешного чтения) при общем количестве копий N. Если выполнено условие W + R > N, то чтение пересекается с записью хотя бы на одном узле, что гарантирует видимость последних записей при чтении (то есть свежезаписанные данные будут прочитаны хотя бы с одной реплики). Например, если данные реплицируются на 3 узла (N=3), можно требовать подтверждение записи от 2 из них (W=2) и при чтении опрашивать тоже 2 узла (R=2). Тогда хотя бы один из двух читающих узлов наверняка содержит запись (потому что запись прошла как минимум на 2 узлах). Такая конфигурация называется строгим кворумом и обеспечивает консистентность чтения при отсутствии новых сбоев. Можно ослаблять требования: например, W=1, R=1 – это минимальные кворумы, при которых система работает сверхбыстро и крайне доступно, но полностью теряет гарантию консистентности (это примерно соответствует просто трем независимым копиям, из которых читаем произвольную). Таким образом, leaderless-модель позволяет гибко настроить баланс между консистентностью и доступностью. В частности, популярные NoSQL-системы предлагают выбор уровня консистентности при каждом запросе.
Поскольку нет лидера, запись реально может поступить сначала на любую реплику. Необходимо потом довести её до остальных копий. Если при записи не все узлы были успешны (например, из 3 два записались, а один не отвечал), «пропущенный» узел всё равно получит данные позже: для этого служат механизмы Hints и Anti-Entropy. Первый означает, что узел-координатор, получив подтверждения от части реплик, запомнит («подскажет») для недоступных узлов изменения и попытается доставить их, когда те появятся в сети. Второй – что периодически все узлы обмениваются контрольными суммами (меркл-деревьями) своих данных и выравнивают недостающие изменения. Благодаря этим фоновым процессам система стремится, чтобы рано или поздно все копии выровнялись, даже если при первичной операции не все получили обновление. Чтения, как уже упомянуто, тоже зачастую обращаются к нескольким узлам. Если один из узлов возвращает более новую версию данных, чем другой, клиент (или координатор) может выполнить read-repair – отправить обратно более свежие данные на отставшую реплику, тем самым отремонтировав её состояние. По сути, в leaderless модели поддержание консистентности происходит постоянно и постепенно: каждый запрос может вносить вклад в согласование копий (чтения чинят данные, фоновые потоки разносят недостающие изменения). Это отличается от резкого переключения лидеров или разрешения конфликтов пакетно; здесь конвергенция данных – непрерывный процесс.
В таких системах обычно отсутствует глобальный порядок операций. Два клиента могут одновременно обновить одну и ту же запись на разных узлах (поскольку любой узел способен принять запись). Поэтому, как и в multi-leader, необходимы подходы к конфликтам. Чаще всего используют сравнительно простое правило «последняя запись побеждает»: каждый клиентский апдейт снабжается временной меткой (timestamp), и при слиянии нескольких изменений система выбирает версию с максимальной меткой как актуальную. Именно так поступает, например, Apache Cassandra – при конфликте по одному ключу он сохранит значение с наиболее новым timestamp, а старое перезапишет (или пометит как удалённое). Это не гарантирует отсутствие потерь (если часы не синхронизированы идеально или обновления были одновременно, одно из них пропадёт), но позволяет обходиться без хранения нескольких версий для простоты и скорости. Альтернативно, некоторые хранилища (как Riak, развитый из идеи Dynamo) поддерживают сохранение сиблингов – нескольких версий значения, если версии конкурируют, предоставляя их на чтение клиенту для ручного разрешения. Но это утяжеляет использование, поэтому Cassandra/Scylla и DynamoDB предпочитают автоматику с метками времени.
Плюсы leaderless-модели:
Максимальная отказоустойчивость и отсутствие центральной точки сбоя. Поскольку любой узел может и принимать данные, и обслуживать запросы, отказ любого отдельного узла практически не влияет на доступность всей системы. Данные реплицируются на нескольких серверах; потеря одного приводит лишь к временной деградации (уменьшается число копий, но если хотя бы одна жива – данные не утеряны). Даже потеря большей части узлов может не прервать работу, пока есть необходимый кворум для операций. Например, при N=3, W=2, R=2 кластер из 3 узлов выдерживает отказ одного узла без потери функциональности – остаются 2, которые могут выполнять и чтения, и записи (2 из 2 даст кворум). А при W=1, R=1 даже отказ 2 узлов из 3 всё еще оставляет систему работоспособной (правда, уже без резервирования). Такая архитектура хорошо подходит для облачных сред, где ноды могут выходить из строя, перезапускаться – система продолжит обслуживание, где бы ни происходили сбои.
Горизонтальная масштабируемость и высокая производительность. Leaderless системы, как правило, строятся на принципах sharding (разделение данных по узлам) и масштабирования горизонтально. Например, Apache Cassandra разбивает все данные по партициям с помощью хеширования ключей, и распределяет партиции между узлами кластера. Каждый узел отвечает за свой диапазон данных. При этом для отказоустойчивости каждая партиция хранится не на одном узле, а на нескольких (репликация фактор N). Такой дизайн позволяет почти линейно увеличивать вместимость и throughput: добавляя новые узлы, вы распределяете партиции (тем самым доля данных на узел уменьшается и нагрузка тоже). В результате крупные инсталляции Cassandra насчитывают сотни узлов, обслуживая огромные объёмы данных и трафика. Важное отличие от master-slave: здесь нет узла, который должен обработать все записи – каждая запись идёт только на узлы, отвечающие за её партицию. Поэтому суммарная пропускная способность системы растёт с увеличением числа узлов. Многие NoSQL базы (DynamoDB, Riak, Voldemort, Scylla) построены по такому принципу и демонстрируют практически неограниченную масштабируемость по мере добавления серверов. Как отмечают разработчики, чтобы удвоить мощность Cassandra, достаточно удвоить число узлов – она масштабируется линейно.
Тонкая настройка консистентности под требования. Leaderless предоставляет разработчикам гибкость выбора. Можно настроить систему на сильную консистентность, установив W и R равными N (т.е. требовать подтверждения от всех реплик для каждого запроса) – тогда поведение будет почти как у CP-системы: ни один узел не потеряет данные, но достаточно потерять один узел, и запись остановится. Наоборот, можно сделать W=1, R=1 – и получить максимальную производительность и доступность, однако пожертвовав свежестью данных (AP-система). Компромиссный вариант – кворум (большинство узлов): он даёт уверенность, что данные не потеряются, если не выйдет из строя больше половины узлов сразу, и что чтения видят более свежие данные, если хотя бы половина узлов жива. Кроме того, в некоторых базах, таких как Cassandra, уровень консистентности выбирается на запрос. Например, вы можете для операций обновления критичных данных требовать QUORUM, а для менее критичных – ONE (минимальная задержка). Такая гибкость позволяет достичь нужного баланса на уровне приложения.
Простота добавления узлов и географическое распределение. В leaderless системе обычно легко включить новый узел: он получает часть партиций и начинает участвовать в репликации. Нет роли «лидера», которую нужно переназначать или каким-то образом настраивать. Это упрощает автоматическое масштабирование. Также эти системы часто топология-агностичны – им всё равно, где узлы географически, можно иметь узлы в разных DC, и репликация настраивается по нужной стратегии (например, Cassandra позволяет распределять реплики так, чтобы копии данных были в разных датацентрах для устойчивости к катастрофе). При этом не требуется сложного механизма конфликтов, как в multi-leader, т.к. по каждому ключу всё равно данные реплицируются через кворумы. По сути, leaderless сочетает преимущества: нет единой точки отказа (как multi-master) и реже возникает необходимость ручного разрешения конфликтов (при правильном настройке кворума, конфликты решаются простым правилом, как timestamp).
Всегда включённая доступность (AP по CAP). Большинство leaderless систем по умолчанию спроектированы как AP-системы согласно CAP. Например, Apache Cassandra позиционируется как Available & Partition-tolerant by default, то есть «всегда включена», жертвуя консистентностью. В практике это означает, что при любых проблемах связи или узлов, кластер старается отвечать на запросы, пусть даже иногда возвращая устаревшие данные, но не «зависая». Для многих приложений (логирование, аналитика, социальные сети) это предпочтительнее, чем строгое, но недоступное хранилище.
Минусы leaderless-модели:
Слабая консистентность по умолчанию (Eventual consistency). Если параметры кворума не выбраны на строгий режим, система будет лишь постепенно приводить данные в согласованное состояние. Это значит, что в любой момент разные клиенты могут видеть разнообращающееся состояние. Например, клиент А обновил значение и получил подтверждение (от минимального кворума), а почти сразу клиент Б прочитал с другого узла, который ещё не получил этот апдейт – клиент Б увидит старые данные. Со временем (через секунды или меньше) запись дойдёт и до узла, который читал Б, и тогда консистентность восстановится, но моментально её нет. Для многих типов данных это приемлемо (постепенное сходжение), но для некоторых – нет. Разработчикам приходится строить приложение с учетом eventual consistency: закладывать, что чтения могут отражать не самый новый commit, что возможно дубликаты (если запись повторно отправилась после сбоя) или что из двух параллельных операций - обе применились.
Сложность программирования согласованности. Хотя конфликты формально решаются просто (по времени), программисту всё равно нужно думать о возможных аномалиях. Например, если две транзакции одновременно увеличивают счетчик на 1, а система взяла и последней оставила только одну (потому что timestamp одной выше) – получится потерянное обновление. Cassandra, например, не обеспечивает атомарности операций инкремента без специальных типов (counter columns). В общем случае, разработчик должен учитывать, что стандартные транзакционные гарантии ACID могут отсутствовать. Многие leaderless DB работают по принципу BASE (Basically Available, Soft-state, Eventually consistent), что требует иной ментальной модели. Нельзя полагаться, что сразу после записи все чтения её увидят; возможно понадобится дополнительная логика ретраев или проверки версий. Для критичных инвариантов (например, уникальность) приходится использовать ухищрения: либо настроить QUORUM/ALL на операции (замедляя их), либо применять внешние механизмы (например, проверять уникальность на уровне приложения, сканируя все реплики). Таким образом, разработка над eventual-consistent хранилищем сложнее, чем над традиционной СУБД, особенно для сложных взаимосвязанных данных.
Стоимость кворумных операций (latency выше, чем у одного лидера). Когда на каждую запись нужно дождаться подтверждения от нескольких узлов, операция длится столько, сколько самый медленный из них (из требуемых). То есть минимальная задержка записи – время сетевого RTT до самого «дальнего» из необходимых реплик плюс время записи на ней. В локальном кластере это миллисекунды, но всё же обычно медленнее, чем запись на один узел. Чтения при R>1 тоже требуют опроса нескольких узлов и сравнения версий, что дольше, чем чтение из одного узла. Впрочем, многие реализации оптимизируют: например, Cassandra при чтении QUORUM запрашивает две копии параллельно и возвращает результат как только получил самую новую версию – то есть latency примерно равно медленнейшему из двух узлов, а не сумме. Но если один узел заметно отстаёт, это может замедлить ответ. В целом, latency кворумных операций немного выше, чем в single-master (особенно в пределах одного датацентра разница невелика, единицы миллисекунд). Если же кворум тянет узлы из разных DC, задержка сильно растёт – поэтому обычно стремятся выполнять операции, требующие согласованности, внутри одного региона.
Потенциальная потеря данных при неудачных настройках. Leaderless система даёт свободу настроек, но неправильный выбор может привести к утере или противоречию данных. К примеру, если администратор выбрал W=1 (запись подтверждается, как только записалась на один узел) для скорости, то при одновременном падении этого узла данные, которые только на нём, потеряются навсегда (ведь другим они не успели разослаться). Или если сделан R=1, можно легко прочитать устаревшее значение. Разработчикам нужно хорошо понимать гарантии и фейлы: условие безопасности – W + R > N – должно обычно выполняться, иначе консистентность не достигнется. Однако даже это не панацея: при W + R > N данные не теряются и свежие чтения будут видеть запись, но если как раз случится разделение сети, меньшая часть узлов может остаться без кворума и отказать в обслуживании. Например, N=5, W=3, R=3: если сеть поделилась 3+2, в части с 2 узлами ни чтения, ни записи кворум не наберут и та часть станет недоступна. То есть доступность падает, хотя консистентность гарантированa для большего компонента. В AP-системах обычно допускают временную неконсистентность, чтобы оба компонента могли продолжать работу (каждый со своими 2 и 3 узлами), но тогда W+R>N нарушается и после слияния получим конфликты. Так что администратор должен выбирать: или пожертвовать операциями в меньшем сегменте (получить CP на время разделения), или пожертвовать консистентностью (получить AP, но потом разрешать конфликт). Как мы видим, эти фундаментальные решения CAP никуда не исчезают, просто leaderless даёт вам инструмент выбирать их динамически.
Отсутствие глобальных транзакций и ограниченные модели данных. Большинство leaderless баз – это NoSQL (ключ-значение, wide-column) хранилища, которые не поддерживают сложных транзакций, join’ов, агрегатов по месту. Они оптимизированы под простые операции чтения/записи по ключу. Если нужно сделать атомарное обновление нескольких записей, часто это невозможно на уровне БД (кроме как вручную реализовать механизм на клиенте). Некоторые современные системы (например, Cassandra c версией 2.0+ или Scylla) ввели легковесные транзакции (Lightweight Transactions) на основе протокола Paxos, что позволяет делать условные обновления с консенсусом (например, «если значение X равно ..., то обновить на ...» атомарно). Однако это довольно дорогие операции и используются редко, для особых случаев. В общем, developer experience с такими базами иной: вы проектируете данные под ограничения eventual consistency и простых операций, что иногда приводит к денормализации, дублированию, отказу от сложных связей — всё во имя масштабирования и доступности.
Типичные случаи использования: Leaderless репликация отлично зарекомендовала себя в масштабных веб-приложениях, где количество запросов огромно, а требуемая консистентность – лишь eventual. Примеры: системы аналитики кликов и логов, мониторинг, хранение социальных графов и лент новостей, рекомендации, IoT-телеметрия. Во многих таких случаях потеря небольшого количества данных или небольшая задержка сходимости не критична, а вот простой системы неприемлем. Apache Cassandra применяется в крупных проектах: в Facebook (для хранения переписки и потоков событий), Netflix, Twitter, Reddit и других, где требуются высокая доступность и масштаб. Amazon DynamoDB – облачный сервис, основанный на похожих принципах, также используется для игровых приложений, ад-тех платформ и т.п., где важна скорость и непрерывность работы. Riak известен кейсами в телекоммуникациях и промышленности (например, хранение данных сенсоров, когда система должна писать новые измерения даже при частичном отключении узлов). Geo-распределенные системы тоже часто строятся на leaderless: можно располагать узлы кластера в разных регионах, и при отключении одного региона остальные продолжат обслуживать запросы (хотя некоторые пользователи могут временно читать устаревшие данные, но сервис будет работать). Например, у Cassandra можно настроить репликацию между несколькими датацентрами; обычно при этом операции на reading/writing настроены на локальный кворум (только внутри текущего DC, чтобы не ждать глобальных узлов), что обеспечивает низкую задержку и доступность локально. Данные межцентрово обмениваются асинхронно. По сути, это гибрид multi-leader (каждый DC пишет самостоятельно) и leaderless (внутри DC). Но благодаря тому, что внутри датацентра соблюдается консистентность, достигается разумный баланс: каждый регион eventual-consistent с остальными, но внутри себя имеет гарантированную консистентность на чтение при CL=QUORUM. Подобные конфигурации успешно используют для глобальных пользовательских сервисов (например, распределённая кеширующая структура или каталог продуктов). Summing up, leaderless отлично подходит, когда масштаб и отказоустойчивость важнее, чем мгновенная согласованность. Если нужно быстро писать/читать огромные объёмы данных и выдерживать выход из строя узлов, а приложение терпимо относится к eventual consistency, то такой подход будет эффективным. Не подходит он для случаев, когда нужна строгая последовательность операций (банковские счета, финансовые транзакции) – хотя и тут можно выкрутиться, но тогда придётся добавить надстройки типа отдельного сервиса консистентности.
CAP, консистентность, доступность: Как уже упоминалось, по умолчанию leaderless системы относятся к классу AP – они обеспечивают высокую доступность и устойчивость к разделениям, жертвуя строгой согласованностью. Например, Apache Cassandra позиционируется как AP-база данных по CAP. Это значит, что при сетевых проблемах Cassandra предпочитает продолжать работать (пусть и не все узлы видят обновления), нежели остановиться в ожидании консенсуса. Консистентность при этом является настраиваемой характеристикой: можно переключить часть функций в CP-режим, потребовав полный кворум, но тогда система перестанет быть вседоступной (при недоступности одного узла уже не будет кворума). Partition tolerance для leaderless — базовое свойство: система спроектирована выдерживать разделение сети и восстановление после него. При разделении обычно большинство узлов может образовать кворум и продолжить работу, а меньшинство может быть недоступно (или тоже обслуживать запросы, если настроено без строгого кворума, но тогда появятся конфликты).
Отказоустойчивость таких систем очень высокая: данные дублируются на многих узлах, поэтому потерять их сложно (надо чтобы вышли из строя все узлы, содержащие копии конкретных данных). При отказе части узлов, кластер автоматически маршрутизирует запросы на оставшиеся копии. Например, если узел не отвечает, координирующий узел не будет ждать его и сразу обратится к другим репликам (в Cassandra есть опция Speculative Retry, когда при медленном ответе одного узла запрос дублируется на другой). В итоге пользователь может даже не заметить, что один из серверов вышел – ответ придёт от другого.
Масштабируемость, latency, разработка (итоги): Leaderless подход обеспечивает выдающуюся горизонтальную масштабируемость, практически не имея центральных узких мест. Это делает его предпочтительным для больших данных и высоконагруженных сервисов. С другой стороны, разработка под eventual consistency требует аккуратности и понимания распределённых алгоритмов. Нередко над подобными хранилищами строят дополнительные уровни: например, кэширование с активной инвалидацией или систему журналирования, чтобы отслеживать подтверждение операций. Иногда совмещают leaderless с сильной консистентностью на уровне отдельных ключей: например, используют ограниченные транзакции (CAS, Paxos) только для ключевых точек (та же Cassandra позволяет выполнять INSERT ... IF NOT EXISTS с консенсусом для гарантий уникальности). По задержкам эти базы обычно оптимизированы для быстрого отклика при небольших кворумах; при высоких требованиях к консистентности задержки растут, но всё еще могут быть лучше, чем тянуться к единому лидеру за океан.
Заключение
Мы рассмотрели три архитектурных подхода к репликации данных – с одним лидером, с несколькими лидерами и без лидера – и их влияние на работу распределённых СУБД. Как же правильно выбрать базу данных для разработки с учётом этих моделей? Однозначного ответа нет – выбор зависит от требований вашего приложения:
Если для вас критически важна строгая согласованность данных и упрощение логики приложения, а нагрузка на запись не превышает возможности одного сервера, то лучшим выбором будет СУБД с классической моделью single-leader (master-slave репликацией). Такие базы (PostgreSQL, MySQL, MongoDB в режиме реплика-сета и др.) обеспечат понятную семантику транзакций. Вы получите сильные гарантии целостности, возможность использовать привычные SQL-функции, а масштабирование чтения – через реплики. Будьте готовы продумать стратегию фейловера и учитывать репликационный лаг, но в остальном разработка будет относительно прямолинейной.
Если ваша система распределена по разным центрам и должна оставаться доступной при отключении связи между ними, либо если пользователи могут вносить изменения офлайн с последующей синхронизацией, – обратите внимание на решения с multi-leader репликацией. Однако применяйте их осторожно: многомастеровые базы (например, CouchDB, специализированные кластеры на основе MySQL Galera или PostgreSQL BDR) требуют глубокого понимания конфликтов. Они хороши для сценариев активных/активных датацентров или peer-to-peer синхронизации, где небольшие рассогласования допустимы ради непрерывной работы. В таких проектах закладывайте время на тестирование конфликтных ситуаций и реализуйте стратегии их автоматического разрешения. Multi-master дает выигрыш, когда нельзя позволить единой точке отказа остановить всю систему, но помните слова из практики: «мульти-мастер – это как грудь ехидны: вроде есть, но пользуются редко». Применяйте его по реальной необходимости и убедившись, что выигрыши перевесят усложнение системы.
Если перед вами стоят задачи web-scale: обслуживание миллионов запросов в секунду, хранение петабайт данных с распределением по множеству узлов, максимальная отказоустойчивость – вероятно, вам подойдёт leaderless база данных. Системы вроде Cassandra, Scylla, Riak или Amazon DynamoDB спроектированы для линейной масштабируемости и доступности. Они эффективны, когда приемлема eventual consistency и требуется работа без перерывов. При выборе такой технологии убедитесь, что модель данных вашего приложения относительно простая (ключ-значение или «широкие» колонки), или что вы можете адаптировать её под ограничения. Например, в проекте интернет вещей, собирающем телеметрию с устройств, Cassandra отлично справится: система всегда будет поглощать новые данные, даже если часть узлов отвалится, а через короткое время все копии выровняются. Но в приложении для банковского учёта применение AP-базы потребует дополнительных мер и, возможно, будет неоправданным. Поэтому тщательно соотнесите CAP-приоритеты вашего приложения с характеристиками модели: если ваше приложение больше терпит недостоверность данных, чем простои – смотрите в сторону AP/leaderless; если наоборот, лучше остановить мир, чем получить противоречивые данные, – выбирайте CP/single-leader решения или вводите слой консенсуса.
Наконец, современные NewSQL и распределённые SQL-базы (Google Spanner, CockroachDB, YugabyteDB и др.) зачастую комбинируют эти подходы: они дают разработчику абстракцию единой SQL-базы, но под капотом используют, например, шардирование с single-leader на шард (через Raft/Paxos консенсус). Это позволяет достичь глобальной конситентности, сохраняя масштабируемость, но и накладывает свои издержки (сложность инфраструктуры, задержки на консенсус). При выборе современной СУБД имеет смысл изучить, какую модель репликации она использует внутри, чтобы понять её сильные и слабые стороны.
Подводя итог, правильно выбрать базу данных — значит сопоставить требования вашей задачи с архитектурой системы хранения. Репликация – фундаментальный механизм, определяющий и отказоустойчивость, и распределённость, и поведение при конкурирующих изменениях. Осознанно подходя к выбору модели репликации, вы как архитектор закладываете основу, от которой зависит успех всей системы. Используйте single-leader там, где ценится простота и консистентность, multi-leader – где нужна непрерывная активность нескольких узлов, leaderless – где масштаб и доступность важнее строгого порядка. Глубокое понимание этих репликационных подходов поможет сделать обоснованный выбор СУБД и спроектировать систему, оптимально отвечающую нуждам вашего проекта.
Ссылки
Kleppmann M. Designing Data-Intensive Applications, O'Reilly, 2017 – Глава 5, «Replication» (пер. на рус. в открытых источниках)
Документация MongoDB – Replica Set Primary (MongoDB Manual) mongodb.com
Документация Apache Cassandra – Replication and Consistency (Cassandra Manual) cassandra.apache.org
Официальный блог Apache Cassandra – Linear Scalability cassandra.apache.org