Перейти к основному содержимому
Версия: 7.0

База данных

Введение

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

Поддерживаемые базы данных:

  • MongoDB — репликационный набор (Replica Set)
  • PostgreSQL — Patroni кластер (начиная с версии 7)
осторожно

Пассворк поддерживает работу с PostgreSQL, но мы не осуществляем техническую поддержку базы данных. Установка, настройка и обслуживание выполняются вашими силами.

Механизм голосования нод

Принцип работы

И MongoDB Replica Set, и PostgreSQL Patroni кластер используют механизм голосования для выбора основной ноды (Primary) и обеспечения консистентности данных.

Как работает голосование:

  1. Каждая нода имеет право голоса — каждая нода в репликационном наборе или кластере может участвовать в выборе Primary
  2. Кворум — для выбора нового Primary необходимо большинство голосов (более 50%)
  3. Автоматический выбор — при сбое текущего Primary ноды автоматически проводят голосование для выбора нового Primary
  4. Синхронизация данных — новый Primary должен иметь актуальные данные (синхронизирован с предыдущим Primary)

Критическая важность количества нод

Минимальное количество нод: 3

Для обеспечения отказоустойчивости необходимо использовать нечётное количество нод (3, 5, 7) в репликационном наборе или кластере.

Почему именно 3 ноды?

  • При 2 нодах: при сбое одной ноды оставшаяся нода не может сформировать кворум (50% голосов недостаточно, нужно >50%)
  • При 3 нодах: при сбое одной ноды оставшиеся 2 ноды формируют большинство (66% голосов) и могут выбрать нового Primary

Почему нечётное количество нод предпочтительнее?

При использовании чётного количества нод (например, 4 ноды) возникает риск ситуации split-brain (разделение мозга):

  • При разделении сети на две равные части (2 ноды и 2 ноды) ни одна часть не может сформировать большинство (50% голосов недостаточно, нужно >50%)
  • Обе части переходят в режим Read-Only, и система становится недоступна
  • Это может произойти при сетевых проблемах, разделении ЦОДов или других сбоях инфраструктуры

Пример проблемы с 4 нодами:

         ┌─────────────────────────────────────────────────────────────────────────┐
│ РАЗДЕЛЕНИЕ СЕТИ (SPLIT-BRAIN) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Node #1 │ │ Node #2 │ │ Node #3 │ │ Node #4 │ │
│ │ │ │ │ │ │ │ │ │
│ │ Голос: 1 │ │ Голос: 1 │ │ Голос: 1 │ │ Голос: 1 │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ │
│ │ │ │
│ Часть 1: 2 ноды (50%) Часть 2: 2 ноды (50%) │
│ — Не может выбрать Primary — Не может выбрать Primary │
│ — Read-Only режим — Read-Only режим │
│ — Пассворк недоступен — Пассворк недоступен │
└─────────────────────────────────────────────────────────────────────────┘

Преимущества нечётного количества нод:

  • При 3 нодах: при разделении сети всегда будет одна часть с большинством (2 ноды = 66% > 50%)
  • При 5 нодах: при разделении сети всегда будет одна часть с большинством (3 ноды = 60% > 50%)
  • При 7 нодах: при разделении сети всегда будет одна часть с большинством (4 ноды = 57% > 50%)

Сравнение конфигураций:

Количество нодСбой 1 нодыРазделение сети (split-brain)Рекомендация
2Read-OnlyRead-OnlyНе рекомендуется
3РаботаетРаботает (2 из 3)Рекомендуется (минимум)
4РаботаетRead-Only (2 и 2)Не рекомендуется
5РаботаетРаботает (3 из 5)Рекомендуется
6РаботаетRead-Only (3 и 3)Не рекомендуется
7РаботаетРаботает (4 из 7)Рекомендуется

Схема голосования:

         ┌─────────────────────────────────────────────────────────────────┐
│ РЕПЛИКАЦИОННЫЙ НАБОР / КЛАСТЕР │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Node #1 │ │ Node #2 │ │ Node #3 │ │
│ │ (Primary) │ │ (Secondary) │ │ (Secondary) │ │
│ │ │ │ │ │ │ │
│ │ Голос: 1 │ │ Голос: 1 │ │ Голос: 1 │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └─────────────────┼─────────────────┘ │
│ │ │
│ Голосование между нодами │
│ (Кворум: 2 из 3 = большинство) │
└─────────────────────────────────────────────────────────────────┘

Сценарии работы

Нормальная работа (3 ноды):

  • Primary обрабатывает все операции записи и чтения
  • Secondary ноды синхронизируют данные с Primary
  • Все ноды могут участвовать в голосовании

Сбой одной ноды (2 ноды остаются):

  • Оставшиеся 2 ноды формируют большинство (66% голосов)
  • Автоматически выбирается новый Primary из оставшихся нод
  • Система продолжает работать в режиме записи и чтения

Сбой двух нод (1 нода остаётся):

  • Оставшийся нода не может сформировать кворум (33% голосов < 50%)
  • Репликационный набор/кластер переходит в режим Read-Only
  • Пассворк становится недоступен для операций записи

Режим Read-Only и недоступность Пассворка

Когда происходит переход в Read-Only

Репликационный набор или кластер переходит в режим Read-Only в следующих случаях:

  1. Недостаточно нод для кворума

    • При сбое более чем половины нод оставшиеся ноды не могут сформировать большинство
    • Например, при 3 нодах: если сломались 2 ноды, оставшаяся 1 нода (33% голосов) не может сформировать кворум
  2. Разделение сети (Network Partition)

    • При разделении сети на части, каждая из которых содержит менее 50% нод
    • Узлы в меньшей части не могут сформировать кворум
  3. Потеря связи с DCS (для Patroni)

    • При потере связи с DCS (etcd/Consul/ZooKeeper) ноды не могут координироваться
  4. Сбой балансировщика трафика (для Patroni)

    • При сбое балансировщика трафика серверы приложений не могут подключиться к кластеру
    • Балансировщик является критически важным компонентом для работы с Patroni кластером

Последствия для Пассворка

Когда база данных в режиме Read-Only:

Пассворк становится полностью недоступен для подключений и работы. Это происходит потому, что любая операция в Пассворке (включая вход в систему, просмотр данных, создание и изменение записей) требует выполнения операций записи в базе данных для создания событий в истории действий. Поскольку база данных находится в режиме Read-Only, эти операции записи невозможны.

Последствия:

  • Пассворк недоступен — система не принимает подключения от пользователей
  • Пользователи не могут войти в систему
  • Все операции заблокированы — невозможно выполнить любые действия (чтение, создание, изменение, удаление)

Сообщения об ошибках:

  • При попытке подключения к Пассворку будет ошибка подключения к базе данных
  • В логах приложения будут ошибки типа "read-only mode" или "no primary available"
  • Пользователи увидят сообщения об ошибке при попытке доступа к системе

MongoDB Replica Set

Архитектура

MongoDB Replica Set состоит из нескольких нод, один из которых является Primary, а остальные — Secondary.

Типы нод:

  • Primary — основной нода, обрабатывающий все операции записи и чтения
  • Secondary — резервные ноды, которые синхронизируют данные с Primary и могут обрабатывать операции чтения
  • Arbiter (опционально) — нода, участвующий в выборе нового Primary при сбое, но не хранящий данных

Архитектура MongoDB Replica Set:

         ┌────────────────────────────────────────────────────────────────┐
│ СЕРВЕРЫ ПРИЛОЖЕНИЯ ПАССВОРК │
│ (Подключение через единую строку подключения) │
└───────────────────────────┬────────────────────────────────────┘

┌───────────────────────────▼────────────────────────────────────┐
│ MONGODB REPLICA SET │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────┐ │
│ │ MongoDB │ │ MongoDB │ │ MongoDB │ │
│ │ Primary │ │ Secondary │ │ Secondary │ │
│ │ │ │ │ │ │ │
│ │ Голос: 1 │ │ Голос: 1 │ │ Голос: 1 │ │
│ │ │ │ │ │ │ │
│ │ Операции: │ │ Операции: │ │ Операции: │ │
│ │ Read/Write │ │ Read (опц.) │ │ Read (опц.) │ │
│ └────────┬─────────┘ └────────┬─────────┘ └──────┬───────┘ │
│ │ │ │ │
│ │ │ │ │
│ └─────────────────────┼───────────────────┘ │
│ │ │
│ Репликация данных (Oplog) │
│ Голосование для выбора Primary │
└────────────────────────────────────────────────────────────────┘

Принцип работы

  1. Операции записи выполняются только на Primary ноде
  2. Oplog (Operation Log) — все операции записи записываются в специальный журнал операций
  3. Синхронизация — Secondary ноды читают Oplog с Primary и применяют операции к своим данным
  4. Голосование — при сбое Primary ноды проводят голосование для выбора нового Primary
  5. Автоматический failover — новый Primary выбирается автоматически из нод с актуальными данными

Строка подключения

Все серверы приложений Пассворка подключаются к Replica Set через единую строку подключения:

mongodb://node1:27017,node2:27017,node3:27017/pw?replicaSet=rs0

MongoDB драйвер автоматически:

  • Определяет текущий Primary нода
  • При сбое Primary определяет новую Primary ноду, выбранную в результате голосования, и начинает обращаться к ней

PostgreSQL Patroni кластер

Архитектура

Patroni — это одно из распространённых open‑source решений для автоматического управления отказоустойчивым кластером PostgreSQL.

Компоненты Patroni кластера:

  • PostgreSQL ноды — Primary и Standby (резервные) ноды
  • Patroni — демон на каждой ноде, управляющий состоянием PostgreSQL
  • DCS (Distributed Configuration Store) — внешнее хранилище конфигурации (etcd, Consul, ZooKeeper) для координации нод
к сведению

Приведённая ниже схема Patroni кластера описывает один из типовых вариантов построения отказоустойчивой PostgreSQL. Конкретная архитектура, параметры репликации, механизм failover и выбор компонентов (DCS, балансировщик и т.п.) подбираются и настраиваются вашей командой / DBA, исходя из требований и инфраструктуры.

Архитектура Patroni кластера:

         ┌─────────────────────────────────────────────────────────────────┐
│ СЕРВЕРЫ ПРИЛОЖЕНИЯ ПАССВОРК │
│ (Подключение через балансировщик трафика) │
└───────────────────────────┬─────────────────────────────────────┘

┌───────────────────────────▼─────────────────────────────────────┐
│ БАЛАНСИРОВЩИК ТРАФИКА │
│ (Nginx / HAProxy) │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Health check для определения Primary ноды │ │
│ │ Направление операций записи на Primary │ │
│ │ Распределение операций чтения (опционально) │ │
│ └──────────────────────────────────────────────────────────┘ │
└───────────┬──────────────────┬──────────────────┬───────────────┘
│ │ │
│ │ │
┌───────────▼──────────────────▼──────────────────▼───────────────────┐
│ PATRONI КЛАСТЕР POSTGRESQL │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ PostgreSQL │ │ PostgreSQL │ │ PostgreSQL │ │
│ │ Primary │ │ Standby │ │ Standby │ │
│ │ (Master) │ │ (Streaming │ │ (Streaming │ │
│ │ │ │ Replication) │ │ Replication) │ │
│ │ │ │ │ │ │ │
│ │ Patroni │ │ Patroni │ │ Patroni │ │
│ │ Голос: 1 │ │ Голос: 1 │ │ Голос: 1 │ │
│ │ │ │ │ │ │ │
│ │ Операции: │ │ Операции: │ │ Операции: │ │
│ │ Read/Write │ │ Read (опц.) │ │ Read (опц.) │ │
│ └────────┬─────────┘ └─────────┬────────┘ └────────┬─────────┘ │
└───────────┼──────────────────────┼────────────────────┼─────────────┘
│ │ │
│ │ │
└──────────────────────┼────────────────────┘

DCS (etcd / Consul / ZooKeeper)
(Координация и выбор лидера)

Балансировщик трафика

Роль балансировщика:

Балансировщик трафика выполняет важную функцию — определяет текущий Primary (Master) нода через health check и направляет на него все операции.

Как это работает:

  1. Health check — балансировщик периодически проверяет состояние каждой ноды PostgreSQL через специальные эндпоинты Patroni
  2. Определение Primary — балансировщик определяет, какой нода является Primary, на основе ответов health check
  3. Маршрутизация записи — все операции направляются только на Primary нода

Поддерживаемые балансировщики:

  • HAProxy — наиболее популярный и рекомендуемый выбор, поддерживает health check через Patroni REST API для автоматического определения Primary ноды
  • Nginx — может использоваться с соответствующими модулями и настройкой health check

Примечание: PgBouncer является connection pooler для PostgreSQL, а не балансировщиком с функциональностью определения Primary через health check. PgBouncer может использоваться в связке с HAProxy или Nginx для пулинга соединений, но сам по себе не решает задачу определения Primary ноды в Patroni кластере.

Пример конфигурации health check для HAProxy:

Балансировщик проверяет эндпоинт Patroni REST API (/master) для определения текущей Primary ноды. Только нода, которая является Primary, будет принимать операции записи. После голосования и выбора новой Primary ноды HAProxy автоматически начинает направлять запросы на новую Primary ноду.

Принцип работы

  1. Координация через DCS — Patroni использует DCS для координации нод и выбора Primary
  2. Streaming Replication — синхронная или асинхронная репликация данных между нодами
  3. Автоматический failover — при сбое Primary выполняется голосование, и Patroni автоматически переводит один из Standby нод в Primary
  4. Голосование — ноды координируются через DCS для выбора нового Primary
  5. Обновление балансировщика — после failover балансировщик автоматически определяет новый Primary через health check

Особенности

  • Требуется балансировщик трафика — необходимо настроить балансировщик с health check для определения Primary ноды
  • Требуется DCS — необходимо развернуть и настроить etcd, Consul или ZooKeeper
  • Требуется опытный DBA — настройка и обслуживание кластера требует знаний PostgreSQL

Требования к размещению нод

Критическая важность независимых площадок

Для обеспечения максимальной отказоустойчивости настоятельно рекомендуется размещать ноды базы данных на трёх независимых физических площадках (ЦОДах).

Почему это важно:

  1. Защита от катастроф — при выходе из строя одного ЦОДа остальные продолжают работать
  2. Независимость инфраструктуры — каждый ЦОД имеет свою инфраструктуру (электроснабжение, охлаждение, сеть)
  3. Географическое распределение — ноды могут быть размещены в разных географических локациях

Рекомендуемая архитектура размещения

         ┌─────────────────────────────────────────────────────────────────────┐
│ РЕКОМЕНДУЕМАЯ АРХИТЕКТУРА │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ ЦОД #1 │ │ ЦОД #2 │ │ ЦОД #3 │ │
│ │ │ │ │ │ │ │
│ │ ┌────────────┐ │ │ ┌────────────┐ │ │ ┌────────────┐ │ │
│ │ │ MongoDB/ │ │ │ │ MongoDB/ │ │ │ │ MongoDB/ │ │ │
│ │ │ PostgreSQL │ │ │ │ PostgreSQL │ │ │ │ PostgreSQL │ │ │
│ │ │ Node #1 │ │ │ │ Node #2 │ │ │ │ Node #3 │ │ │
│ │ └────────────┘ │ │ └────────────┘ │ │ └────────────┘ │ │
│ │ │ │ │ │ │ │
│ │ Независимая │ │ Независимая │ │ Независимая │ │
│ │ инфраструктура │ │ инфраструктура │ │ инфраструктура │ │
│ └────────┬─────────┘ └────────┬─────────┘ └───────┬──────────┘ │
│ │ │ │ │
│ │ │ │ │
│ └─────────────────────┼────────────────────┘ │
│ │ │
│ Высокоскоростная сеть │
│ (для репликации данных) │
└─────────────────────────────────────────────────────────────────────┘

Требования к сетевому соединению

Узлы базы данных должны иметь:

  • Высокоскоростное соединение между собой для репликации данных
  • Низкая задержка (latency) — для обеспечения быстрой синхронизации
  • Стабильное соединение — минимизация потерь пакетов
  • Достаточная пропускная способность — для передачи данных репликации

Минимальные требования

Минимум: 3 ноды на 3 независимых площадках

  • Каждый нода на отдельной физической площадке (ЦОД)
  • Высокоскоростное сетевое соединение между площадками
  • Независимая инфраструктура на каждой площадке

Альтернативный вариант (не рекомендуется):

  • 3 ноды в одном ЦОДе, но на разных серверах/стеллажах
  • Меньшая защита от катастроф, но всё ещё обеспечивает отказоустойчивость при сбое одной ноды

Подключение серверов приложений

Единая строка подключения

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

MongoDB Replica Set:

MongoDB драйвер автоматически определяет Primary нода, поэтому можно указывать все ноды в строке подключения:

mongodb://mongodb://db-mongo-1,db-mongo-2,db-mongo-3/?replicaSet=rs0

PostgreSQL Patroni:

warning

Для Patroni кластера подключение осуществляется через балансировщик трафика, а не напрямую к нодам.

Балансировщик трафика определяет текущий Primary нода через health check и направляет операции записи на него. Строка подключения указывает на балансировщик:

postgresql://user:password@balancer:5432/pw?serverVersion=xx&charset=utf8

Где balancer — адрес балансировщика трафика (HAProxy или Nginx), который автоматически маршрутизирует запросы к текущей Primary ноде.

Автоматическое определение Primary

MongoDB Replica Set

MongoDB драйвер автоматически:

  1. Определяет текущий Primary при подключении
  2. Мониторит состояние нод
  3. После выборов переключает запросы на новую Primary-ноду

PostgreSQL Patroni

Балансировщик трафика по health check к Patroni REST API:

  1. Определяет актуальный Primary
  2. Мониторит состояние нод
  3. После failover переводит трафик на новую Primary-ноду

Рекомендации

Для MongoDB Replica Set:

  • Используйте единую строку подключения на всех серверах приложений
  • Не указывайте конкретный нода — всегда указывайте все ноды в строке подключения
  • Настройте таймауты — установите разумные таймауты для подключения и операций

Для PostgreSQL Patroni:

  • Используйте балансировщик трафика — подключение должно идти через балансировщик, а не напрямую к нодам
  • Настройте health check — балансировщик должен проверять состояние нод через Patroni REST API
  • Используйте единую строку подключения — все серверы приложений должны подключаться к одному адресу балансировщика
  • Настройте таймауты — установите разумные таймауты для подключения и операций
  • Мониторьте балансировщик — отслеживайте, к какой ноде балансировщик направляет запросы