Отказоустойчивые решения
Кластер BiHA
16
Авторские права
© Postgres Professional, 2023–2026
Авторы: Алексей Береснев, Илья Баштанов, Павел Толмачев, Игорь
Гнатюк
Фото: Олег Бартунов (монастырь Пху и пик Бхрикути, Непал)
Использование материалов курса
Некоммерческое использование материалов курса (презентации,
демонстрации) разрешается без ограничений. Коммерческое
использование возможно только с письменного разрешения компании
Postgres Professional. Запрещается внесение изменений в материалы
курса.
Обратная связь
Отзывы, замечания и предложения направляйте по адресу:
edu@postgrespro.ru
Отказ от ответственности
Компания Postgres Professional не несет никакой ответственности за
любые повреждения и убытки, включая потерю дохода, нанесенные
прямым или непрямым, специальным или случайным использованием
материалов курса. Компания Postgres Professional не предоставляет
каких-либо гарантий на материалы курса. Материалы курса
предоставляются на основе принципа «как есть» и компания Postgres
Professional не обязана предоставлять сопровождение, поддержку,
обновления, расширения и изменения.
2
Темы
Кластер BiHA
Архитектура и принцип работы
Конфигурация
Создание кластера
Режимы репликации
Отработка отказов
Выборы
Режим 2+1
3
Кластер BiHA
Кластер физической репликации
синхронная и асинхронная репликация
масштабируемость по операциям чтения
Встроенная высокая доступность
автоматическое обнаружение и отработка отказа узла
кворум на базе алгоритма консенсуса Raft
не требует внешнего управления кластером
Работает на Linux-платформах
Расширение BiHA (Built-in High Availability) позволяет организовать
отказоустойчивый кластер физической репликации, включающий в себя
выделенный узел-лидер и несколько узлов-последователей. Лидер
передает потоки репликации на узлы-последователи, доступные только
для чтения. Репликация может быть синхронной или асинхронной.
Балансировка запросов на чтение между несколькими узлами
позволяет увеличить пропускную способность системы.
Клиентские соединения могут направляться на определенные узлы
внешним балансировщиком или пулером. В версии Postgres Pro
Enterprise 17 для этого используют расширение Proxima.
BiHA обеспечивает отказоустойчивость за счет автоматического
выявления отказа узла и изменения конфигурации кластера,
означающей автоматическое переключение ролей узлов и
восстановление данных на вернувшихся в строй узлах. При отказе
лидера новый выбирается на основе кворума с использованием
алгоритма консенсуса Raft. Ручное изменение конфигурации узлов
также возможно без остановки обслуживания.
BiHA не использует внешнее программное обеспечения для управления
В настоящее время BiHA не работает в среде MS Windows.
4
Архитектура
ro
Postgres Pro
(расширение
+ набор патчей
ядра)
r/w
обмен по
управляющему
каналу
физическая
репликация
лидер
последователь
ro
последователь
Расширение BiHA включает в себя доработанное ядро СУБД,
SQL-интерфейс управления и сам код расширения.
На каждом из узлов запускаются служебные процессы BiHA worker,
координирующие работу кластера. Эти процессы обмениваются
служебной информацией, используя BiHA Control Protocol (BCP). Сами
данные с узла-лидера передаются на последователи с помощью
штатного механизма физической репликации (поток WAL-записей).
Для инициализации кластера и управления им используется утилита
bihactl. Она может создавать узел-лидер, добавлять узлы-
последователи, преобразовывать уже существующий экземпляр в узел-
лидер или узел-последователь в кластере, а также проверять статус
узлов кластера.
5
Параметры конфигурации
большинство
(кворум)
biha.nquorum = 3
biha.minnodes = 3
При создании узлов кластера с помощью утилиты bihactl, кроме
очевидных параметров (адрес, порт, каталог данных) нужно указать
параметры, относящиеся ко всему кластеру. Самые важные из них:
●
biha.nquorum — минимальное число узлов, при котором проводятся
выборы лидера;
●
biha.minnodes — минимальное число узлов, при котором лидер
изменяет данные.
Если biha.nquorum или более связанных узлов остались без лидера,
они проводят выборы нового лидера. Поведение текущего лидера от
этого параметра не зависит.
Если лидер находится в группе из не менее чем biha.minnodes узлов,
он обрабатывает запросы на чтение и запись, а при меньшем числе
узлов — только на чтение.
По умолчанию для biha.minnodes берется значение biha.nquorum.
Чтобы избежать ситуации split-brain, оба параметра рекомендуется
задать как (общее число узлов + 1) / 2, округленное в большую сторону.
Тогда при разделении сети лидер, оказавшись в меньшинстве,
перестанет принимать запросы на запись, а большинство узлов
выберет нового лидера. Однако при четном числе узлов имеет смысл
задать biha.minnodes как ровно половину от общего числа узлов, чтобы
разделение на две равные части не повлияло на работу лидера.
6
Инициализация кластера
ro
лидер
Поколение 0: {1}
biha.minnodes = 2
1
Кластер BiHA можно получить из существующей системы,
использующей физическую репликацию, но мы создадим новый
трехузловой кластер с нуля.
Первый узел будущего кластера необходимо инициализировать
командой bihactl init. Будет создан экземпляр со служебной базой
данных biha_db и схемой biha, для него будут установлены
необходимые конфигурационные параметры, а также создан слот
репликации.
Поскольку мы рассчитываем на три узла, параметр biha.nquorum
установим равным двум (параметр biha.minnodes получит то же
значение).
Созданный узел станет лидером, но перейдет в режим только чтения,
поскольку находится в одиночестве.
Далее мы будем встречаться с понятием номера поколения кластера
(term). Текущее состояние соответствует нулевому поколению, в него
входит единственный узел. Номер поколения автоматически
увеличивается при изменении состояния кластера, например, при
удалении или добавлении узла, при смене лидера.
8
Добавление узла
r/w
лидер
ro
Поколение 0: {1}
Поколение 1: {1,2}
1
2
последователь
biha.minnodes = 2
Новый узел добавляется в кластер командой bihactl add, выполняемой
на подключаемом узле; при этом в кластере будет сформировано новое
поколение. Когда число узлов станет равным значению параметра
biha.minnodes, кластер станет полностью работоспособным: его лидер
сможет работать на запись.
Перед созданием кластера необходимо обеспечить сетевую связность
и настроить на всех узлах синхронизацию времени.
Как говорилось, мы планируем развернуть трехузловой кластер —
именно такая конфигурация обычно и эксплуатируется. Но что, если
в нашем распоряжении только два узла? Какие значения параметров
можно выбрать?
При biha.nquorum = 2 и biha.minnodes = 2 потеря любого узла приведет
к недоступности кластера на запись.
Если установить biha.nquorum = 1 и biha.minnodes = 1, при разделении
сети между двумя работоспособными узлами последователь станет
лидером — возникнет ситуация split-brain с двумя работающими
лидерами. Это самая плохая ситуация.
Если же установить biha.nquorum = 2 и biha.minnodes = 1, то отказ
последователя не нарушит работу лидера, и при разделении сети split-
brain не возникнет. Но при отказе лидера последователь не станет
новым лидером.
10
Три узла
ro
r/w
лидер
ro
Поколение 0: {1}
Поколение 1: {1,2}
Поколение 2: {1,2,3}
3
1
2
последовательпоследователь
Добавляя дополнительные узлы в кластер, мы избавляемся от
недостатка двухузловой конфигурации: в ней невозможно продолжить
запись после потери сетевой связности без риска split-brain. Трех узлов
уже достаточно для того, чтобы кластер оставался полностью
работоспособным после одного любого сбоя.
В такой конфигурации один из узлов является лидером, остальные —
последователями. При отказе лидера новый будет автоматически
избран на основе кворума. Лидер также может быть назначен вручную.
12
Режимы репликации
асинхронная
синхронная
кворумная
(строгая)
синхронная
кворумная
(нестрогая)
set_sync_standbys( N )
set_sync_standbys_min( M )
set_sync_standbys_min( −1 )
synchronous_standby_names = ANY N MIN M
(biha_node1, biha_node2, ...)
add_to_ssn(),
remove_from_ssn(),
set_ssn(), get_ssn()
По умолчанию кластер BiHA использует асинхронную репликацию,
но можно использовать и синхронную кворумную — задав ее при
инициализации кластера или перейдя на нее в существующем
кластере.
Синхронизацией управляет параметр synchronous_standby_names: при
фиксации транзакции лидер будет ожидать подтверждения от любых N
(ANY N) узлов из списка синхронных. Синхронный режим включается
функцией biha.set_sync_standbys, которая и устанавливает число N.
Если временно доступно менее N синхронных узлов, фиксация
транзакций будет приостановлена. Чтобы продолжить работу в случае
временной недоступности синхронных узлов, можно включить
нестрогую кворумную синхронную репликацию. Тогда лидер будет
продолжать подтверждать транзакции, пока к нему подключено как
минимум M синхронных узлов (MIN M). Значение устанавливается
функцией biha.set_sync_standbys_min, а значение −1 возвращает
кластер к строгой синхронной репликации.
После включения синхронной репликации перейти на асинхронную уже
нельзя, но можно изменить список синхронных узлов функциями
biha.set_ssn, biha.remove_from_ssn и biha.add_to_ssn. Узлы, не
входящие в список, будут работать асинхронно.
14
Отказ последователя
ro
r/w
1
23
последователь последователь
C
O
M
M
I
T
A
C
K
C
O
M
M
I
T
лидер
synchronous_standby_names =
ANY 2
Если последователь был настроен как асинхронный, то отказ одного
узла никак не отразится на лидере, кластер продолжит свою работу.
Если же используется синхронная репликация, то при настройке
synchronous_standby_names = ANY 2 отказ узла приведет
к невозможности завершения транзакции: лидер должен дождаться
подтверждения от двух узлов-последователей, а работает только один.
Чтобы продолжить работу в такой ситуации, можно включить нестрогую
синхронную репликацию ANY 2 MIN 1.
16
Отказ последователей
ro
лидер
1
23
biha.minnodes = 2:
кластер
неработоспособен
последователь последователь
При отказе второго последователя оставшийся в строю лидер
перестанет выполнять запросы, изменяющие данные (запросы на
чтение будут продолжать работать).
18
Отказ лидера
ro
лидер
ro
1
23
последователь последователь
При отказе лидера выполняется автоматическое повышение статуса
одного из последователей по результатам выборов.
Применяемый в выборах алгоритм консенсуса RAFT гарантирует
однозначное определение нового лидера. Выборы проводятся
в группах узлов, составляющих кворум; чтобы предотвратить ситуацию
split-brain, значение параметра biha.nquorum должно превышать
половину узлов.
В процессе выборов узлы выдвигают себя в качестве кандидатов на
роль лидера и запрашивают голоса других узлов. Фактор победы
в голосовании — наличие на узле наиболее актуальных данных,
то есть новым лидером кластера становится последователь
с наибольшим числом записей в WAL. В синхронном кластере можно
задать для узлов приоритет, определяющий порядок выдвижения на
роль лидера: для этого в параметре biha.node_priority узла указывается
тайм-аут, по истечении которого узел предложит себя в качестве
кандидата.
При необходимости можно вообще запретить узлу становиться
лидером или участвовать в голосовании — для этого существуют
параметры biha.can_be_leader и biha.can_vote.
19
Новый лидер
ro
лидер
r/w
лидер
1
23
выборы
или вручную
Поколение 0: {1}
Поколение 1: {1,2}
Поколение 2: {1,2,3}
Поколение 3: {2,3}
вызов
callback-функции
последователь
Помимо автоматического переключения узлов, BiHA поддерживает
ручное переключение. Вручную назначить нового лидера можно
с помощью функции biha.set_leader; ее рекомендуется вызывать на том
узле, который планируется сделать лидером.
При изменении состояния кластера есть возможность вызывать на
узлах заранее зарегистрированные пользовательские функции —
обработчики событий. Это функции для обратного вызова (callback-
функции), которые могут выполнить дополнительные действия,
например, отправить оповещение администратору, уведомить
об изменениях внешнее ПО, управляющее подключением клиентов
(различные прокси и балансировщики), или обеспечить ограждение
(fencing) старого лидера.
20
Возвращение узла
ro
r/w
лидер
1
23
синхронизация
pg_rewind
Поколение 0: {1}
Поколение 1: {1,2}
Поколение 2: {1,2,3}
Поколение 3: {1,2,3}
ro
последователь
последователь
После переключения ролей узлов BiHA выполняет синхронизацию
данных кластера в изменившейся конфигурации. Чаще всего на
последователях требуется применение записей WAL, полученных с
нового лидера. Однако в случае, когда в состав кластера возвращается
старый лидер, может потребоваться обратное действие — его «откат»
до состояния нового, который выполняется утилитой pg_rewind.
Автоматическое выполнение отката определяется параметром
biha.autorewind и по умолчанию выключено. Так сделано потому, что
существует риск потери пользовательских транзакций в журнале
предзаписи на синхронизируемом узле. Кроме этого, при неудачной
автоматической синхронизации узел может перейти в состояние
ошибки, и для возвращения его в строй потребуется вмешательство
администратора.
22
Режим 2+1
ro
узел без
пользовательских
данных
r/w
лидер
рефери
журнал
только в режиме
referee_with_wal
последователь
В кластере можно создать узел-рефери, который будет участвовать
в выборах узла-лидера и поможет избежать проблемы split-brain,
которая возможна, если лидер и единственный последователь
перестают видеть друг друга. При инициализации узла-рефери на нем
создаются лишь служебная база biha_db и системные таблицы;
пользовательские данные на узел не копируются. Поэтому для рефери
требуется меньше дискового пространства, ресурсов процессора и
памяти.
Режим работы узла-рефери определяется параметром --mode команды
bihactl add. Поддерживаются два режима:
●
referee. Узел только принимает участие в выборах лидера,
в синхронной репликации не участвует.
●
referee_with_wal. Узел принимает участие в выборах лидера и
участвует в репликации данных — получает все журналы с лидера.
Если в момент начала выборов больше всего записей будет на
узле-рефери, то узлы-последователи будут пытаться получать с него
недостающие журнальные файлы.
В обоих режимах узел-рефери отправляет и получает сообщения
о контроле состояния по каналу управления и поддерживает функции
мониторинга кластера.
Рефери — это конечное состояние узла: он не может стать ни лидером,
ни последователем.
24
Итоги
Postgres Pro BiHA — кластерное решение для высокой
доступности на основе физической репликации
Не требует установки дополнительного ПО
Автоматически переключает и восстанавливает узлы
при сбоях
Допускает гибкую настройку для достижения баланса
производительности, надежности и стоимости системы
25
Практика
1. Настройте синхронную физическую репликацию между
двумя серверами.
2. Преобразуйте ведущий узел в лидера синхронного
трехузлового BiHA-кластера. Проверьте его статус.
3. Преобразуйте ведомый узел (реплику) в узел-последователь
и проверьте статус кластера.
4. Добавьте в кластер еще один узел-последователь,
работающий как асинхронная реплика. Проверьте статус
кластера и работу репликации. Остановите этот узел и
убедитесь в том, что это не мешает фиксации транзакций.
1. Создайте два дополнительных экземпляра Postgres Pro Enterprise:
для ведущего выполните инициализацию с помощью initdb, а ведомый
создайте из копии ведущего, созданной pg_basebackup.
2. Используйте команду bihactl init c параметрами --convert
и --sync-standbys=1.
3. Для добавления последователя используйте команду bihactl add
c параметрами --convert-standby и --use-leader.
4. Для добавления последователя используйте команду bihactl add
c параметрами --convert-standby и --use-leader, а затем переведите его в
асинхронный режим функцией biha.remove_from_ssn.