Перейти к основному содержимому
Версия: 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://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
  • Используйте единую строку подключения — все серверы приложений должны подключаться к одному адресу балансировщика
  • Настройте таймауты — установите разумные таймауты для подключения и операций
  • Мониторьте балансировщик — отслеживайте, к какой ноде балансировщик направляет запросы