Кластерные технологии
Обзор
16
Авторские права
© Postgres Professional, 2017–2025
Авторы: Егор Рогов, Павел Лузанов, Илья Баштанов, Алексей Береснев
Фото: Олег Бартунов (монастырь Пху и пик Бхрикути, Непал)
Использование материалов курса
Некоммерческое использование материалов курса (презентации,
демонстрации) разрешается без ограничений. Коммерческое
использование возможно только с письменного разрешения компании
Postgres Professional. Запрещается внесение изменений в материалы
курса.
Обратная связь
Отзывы, замечания и предложения направляйте по адресу:
edu@postgrespro.ru
Отказ от ответственности
Компания Postgres Professional не несет никакой ответственности за
любые повреждения и убытки, включая потерю дохода, нанесенные
прямым или непрямым, специальным или случайным использованием
материалов курса. Компания Postgres Professional не предоставляет
каких-либо гарантий на материалы курса. Материалы курса
предоставляются на основе принципа «как есть» и компания Postgres
Professional не обязана предоставлять сопровождение, поддержку,
обновления, расширения и изменения.
2
Темы
Ожидания от кластера
Средства реализации
Компоненты, необходимые для построения кластера
Примеры систем
3
Ожидания от кластера
Высокая доступность системы
в том числе отказоустойчивость
Масштабируемость
Согласованность данных
4
Высокая доступность
Обеспечение минимального времени простоя системы
вызванного как сбоями, так и плановыми работами
Различные характеристики
отношение времени работы системы к общему времени → 100 %
MTBF — время наработки на отказ → ∞
RTO — целевое время восстановления → 0
RPO — целевая точка восстановления → 0
и другие
Доступность системы — отношение времени, в течение которого
система сохраняет доступность, к общему времени эксплуатации.
Понятие доступности более широкое, чем отказоустойчивость,
поскольку простои могут быть вызваны не только отказами, но и
плановыми работами.
Часто говорят о доступности:
●
«одна девятка» 90 % — 36,5 дней простоя в год;
●
«две девятки» 99 % — 3,65 дней простоя в год;
●
«три девятки» 99,9 % — 8,76 часов простоя в год;
●
«четыре девятки» 99,99 % — 52,56 минут простоя в год;
●
«пять девяток» 99,999 % — 5,26 минут простоя в год и т. д.
Высокая доступность предполагает цифры, близкие к 100 %.
Для примера: сервис управляемых баз данных PostgreSQL в Yandex
Cloud обещает доступность узлов на чтение 99,99 %, на запись —
Другими характеристиками доступности могут быть:
●
время наработки на отказ (Mean Time Between Failures);
●
целевое время восстановления (Recovery Time Objective) — время,
за которое система должна восстановиться;
●
целевая точка восстановления (Recovery Point Objective) — период,
за который можно потерять данные при сбое, и др.
5
Масштабируемость
Масштабируемость
способность системы справляться с увеличением рабочей нагрузки
при добавлении ресурсов
идеальная масштабируемость по времени отклика —
время отклика не должно увеличиваться
идеальная масштабируемость по пропускной способности —
число обрабатываемых запросов должно расти линейно
Ускорение
отношение времени отклика на одном узле ко времени отклика
в кластере из N узлов
Важным понятием для кластера является масштабируемость: это
свойство показывает, как меняется целевая характеристика системы
с увеличением рабочей нагрузки при добавлении ресурсов. В контексте
кластера растущая рабочая нагрузка можно вызываться не только
увеличением количества запросов в единицу времени, но и
увеличением объема данных. Под ресурсами мы будем в первую
очередь понимать количество узлов кластера.
Масштабируемость по времени отклика показывает, как меняется
время отклика (то есть время выполнения запроса) с ростом нагрузки
при добавлении узлов кластера. Эта характеристика особенно важна
для OLTP-систем. В идеале время отклика не должно увеличиваться.
Можно говорить не только о масштабируемости по времени отклика,
но и об ускорении, которое показывает, как время отклика меняется
с увеличением числа узлов при фиксированной нагрузке.
Масштабируемость по пропускной способности показывает, как
меняется число обрабатываемых запросов с ростом объема данных
при добавлении узлов кластера. Эта характеристика важна для OLAP-
систем. В идеале число обрабатываемых запросов должно
увеличиваться линейно с ростом числа узлов.
Идеальная масштабируемость (увы, не достижимая на практике)
позволяет решать проблемы роста простым добавлением новых узлов
в кластер.
6
Согласованность
Локальные транзакции
практически никаких гарантий при переключении между узлами
согласованность обеспечивает приложение
Глобальные (распределенные) транзакции
ACID-согласованность
проблема атомарности транзакций
Чем более строгие гарантии согласованности данных предоставляет
кластер, тем больше он похож на «единой целое», тем проще с ним
работать, тем меньше надо учитывать особенности в прикладном коде.
Но тем дороже это обходится в смысле производительности.
Если кластер основан на схеме «мастер-реплика» с одним пишущим
узлом, и транзакции выполняются локально на одном из узлов,
гарантии могут оказаться очень слабыми. Например, один узел мог
успеть применить запись о фиксации транзакции и ее результат уже
доступен клиентам, а другой узел — еще нет. В таком случае у клиента,
в частности, нет гарантии, что он не прочитает старое значение
(на одном узле) уже после того, как прочитал новое (на другом узле).
Обработка таких ситуаций ложится на приложение, которое должно
быть рассчитано на работу в этой конфигурации.
Если кластер предоставляет глобальные (распределенные)
транзакции, то можно говорить о согласованности в обычном смысле
ACID: глобальная транзакция должна переводить базу данных из
одного согласованного состояния в другое согласованное (C), при
условии, что транзакция полностью выполняется на всех узлах (A) при
отсутствии помех со стороны других конкурентных транзакций (I).
В этом случае требуется обеспечить атомарность транзакций: все
узлы кластера должны выполнить одинаковое действие — либо
применить, либо отменить транзакцию. PostgreSQL предоставляет для
этого относительно простой протокол двухфазной фиксации (2PC),
который, однако, не устойчив к сбоям.
7
Средства реализации
Обеспечение высокой доступности
Синхронизация данных между узлами
Виды сбоев и их обнаружение
Распределенные протоколы консенсуса
Обеспечение масштабируемости
8
Высокая доступность
Плановые работы без прерывания обслуживания
средствами PostgreSQL
временный вывод узла из кластера
Дублирование компонентов, исключение точек отказа
серверы PostgreSQL (и другие необходимые системы)
сетевое оборудование
электропитание
Обнаружение сбоев и управление восстановлением
Время простоя может быть вызвано не только возникающими
неполадками, но и плановыми работами, которые невозможно
выполнить без прерывания обслуживания. С каждой версией
PostgreSQL уменьшается число действий, требующих перезагрузки
сервера. Кроме того, кластер должен уметь отрабатывать плановый
останов одного из серверов с минимальными проблемами для
пользователей.
Основа обеспечения отказоустойчивости — дублирование всех узлов
системы. Речь не только о серверах баз данных, но и о сетевом
оборудовании, обеспечении электропитания и пр.
Разумеется, недостаточно просто дублировать компоненты системы.
Во-первых, нужно наладить синхронизацию данных между базами.
Во-вторых, нужно обеспечить обнаружение сбоев в работе системы
и их обработку — корректное переключение на резервные узлы и
возвращение узлов в строй после устранения неисправности.
Большая часть необходимых средств отсутствует в стандартном
PostgreSQL или вообще относится к другим компонентам. В целом, чем
более доступной должна быть система, тем сложнее она будет
устроена.
9
Синхронизация
Shared nothing
физическая репликация
логическая репликация
сторонние таблицы, postgres_fdw
Shared disk
синхронизация не нужна, но требуется отказоустойчивое хранилище
нет штатных средств работы с общим диском
Штатным образом объединение нескольких серверов PostgreSQL
возможно только в архитектуре shared nothing: серверы не имеют ни
общего диска, ни общей памяти, а связываются исключительно по сети.
Для синхронизации данных используется репликация, рассмотренная
в подробностях ранее в этом курсе.
Физическая репликация в силу своей однонаправленности позволяет
строить кластер с одним ведущим узлом и несколькими репликами.
Более гибкая логическая репликация позволяет организовать как
двунаправленную синхронизацию, так и выборочную репликацию
отдельных таблиц или частей таблиц. На логической репликации можно
строить геораспределенные кластера, в которых несколько серверов
работают независимо, но обмениваются изменениями.
Для выборочной репликации можно использовать и другие средства,
например, функционал сторонних таблиц с помощью расширения
postgres_fdw.
Архитектура shared disk с общим дисковым хранилищем дает
интересные возможности, хотя и требует изменений в ядре PostgreSQL.
При общем диске синхронизация не нужна (само хранилище, конечно,
может использовать блочную репликацию для отказоустойчивости, но
для СУБД оно представляется единым устройством), а вычислительные
узлы, не имеющие состояния, требующего надежного хранения, могут
запускаться и останавливаться в любой момент. Такая схема позволяет
динамически подстраиваться под нагрузку, что особенно важно при
работе в облачной инфраструктуре.
10
Отказ узла
обнаружение отказа:
периодический обмен сообщениями
(heartbeat)
Прежде чем говорить об обнаружении и управлении сбоями, нужно
определиться, что считать сбоем. С точки зрения программного
обеспечения кластера обычно рассматривают два вида сбоев: отказ
узла и разделение сети (network partitioning).
При отказе узла этот узел перестает функционировать как часть
кластера. Отказ может быть вызван остановом или сбоем сервера
СУБД, сбоем операционной системы, выключением сервера и т. п.
Для обнаружения сбоев узлы кластера периодически обмениваются
короткими сообщениями (heartbeat). Если какой-либо из узлов не
отвечает в течении определенного времени, фиксируется и
отрабатывается сбой этого узла.
Процедура отработки отказа зависит от архитектуры кластера.
Например, в кластере, построенном на схеме «мастер–реплика», при
сбое мастера какая-то из реплик должна занять его место (должно
произойти переключение ролей).
11
Разделение сети
split-brain: кластер распадается
на независимые, одновременно работающие
части, принимающие запросы на запись
отказ узла, задержка или отказ сети
неотличимы друг от друга
Разделение сети происходит при сбое сетевого оборудования. При этом
все узлы могут сохранить работоспособность, но одна группа теряет
связь с другой группой узлов кластера.
Самая неприятная ситуация возникает в случае, когда обе группы
сохраняют связь с клиентами. Это может привести к проблеме split-
brain: кластер распадается на две части, каждая из которых начинает
работать автономно и при этом принимать запросы на запись.
Возникает рассогласование данных, и при восстановлении данные
одной из групп будут вынуждено потеряны.
Заметим, что сеть может выйти из строя, а может временно начать
терять пакеты или доставлять их с задержкой (более того, в некоторых
ОС обычная загруженность процессора тоже может приводить
к сетевым задержкам). Все эти случаи неотличимы от сбоя узла: если
от узла вовремя не поступит подтверждение, кластер должен считать
такую ситуацию сбоем. Узел может сохранять работоспособность или
нет, но узнать это невозможно, если с ним нет связи.
Многие системы будут нестабильно работать в условиях ненадежного
сетевого подключения. Чтобы уменьшить вероятность ложных
срабатываний, можно увеличить пороговое значение, но тогда
увеличится и время обнаружения настоящего сбоя, а это приведет
к снижению доступности. Для примера: распределенное хранилище пар
ключ–значение etcd предлагает устанавливать порог в 10 раз выше,
чем время нормального отклика узла.
12
Проблема консенсуса
Узлы кластера должны уметь приходить к общему мнению
общее представление о текущем состоянии всех узлов кластера
атомарная фиксация глобальных транзакций
Консенсус через обмен сообщениями
нет общей памяти (shared nothing)
используются специальные распределенные протоколы,
учитывающие возможность сбоев на каждом этапе переговоров
построение, доказательство корректности и тестирование реализации —
сложная задача!
Кластер, составленный из независимых узлов, должен работать —
в некотором смысле — как единое целое. Чтобы управлять сбоями,
узлы должны разделять общее представление о состоянии всех узлов
кластера. Чтобы принять решение о фиксации распределенной
транзакции, все узлы кластера должны согласиться, что это возможно,
и в итоге выполнить фиксацию атомарно.
Поскольку никакого общего устройства хранения у узлов нет (мы
рассматриваем системы класса shared nothing), то единственный
способ прийти к общему мнению — договориться, используя какой-либо
протокол распределенного консенсуса. Протокол определяет, какими
сообщениями и в каком порядке должны обменяться узлы, чтобы
прийти к соглашению.
Важный момент: распределенные алгоритмы очень сложны, поскольку
учитывают множество граничных случаев. Например, они должны
корректно работать в ситуациях, когда на любом этапе переговоров
происходит какой-либо сбой. Доказательство корректности таких
алгоритмов и тестирование их реализаций — также очень сложная
задача. Поэтому не стоит пытаться создать собственный алгоритм или
доверять неизвестному.
13
Протоколы 2PC, 3PC
Двухфазная фиксация
prepare: координатор проводит голосование;
commit: если все «за», координатор сообщает о фиксации.
поддерживается PostgreSQL (PREPARE TRANSACTION + COMMIT)
Трехфазная фиксация
prepare: координатор проводит голосование;
precommit: если все «за», координатор сообщает всем о намерении;
commit: если все успешно, координатор сообщает о фиксации.
Свойства
возможны ситуации (например, разделение сети),
в которых невозможно разрешить транзакцию до устранения сбоя
Простой протокол для фиксации глобальных транзакций, поддержка
которого встроена в PostgreSQL, — двухфазная фиксация (2-Phase
Commit). Узел, выполняющий транзакцию, считается координатором.
На подготовительной фазе (prepare) координатор сообщает остальным
узлам о фиксации транзакции и дожидается от них подтверждения, что
они готовы выполнить фиксацию (если хотя бы один узел не готов —
транзакция обрывается). На фазе фиксации (commit) координатор
сообщает всем узлам о решении зафиксировать транзакцию. При
получении от всех подтверждения, координатор тоже фиксирует
транзакцию.
Этот протокол позволяет обойтись минимумом сообщений, но не
устойчив к сбоям. Например, при отказе координатора после фазы
prepare, остальные узлы не имеют информации о том, должна ли
транзакция быть зафиксирована или отменена. Им придется ждать
устранения сбоя.
Это ограничение преодолевает более сложный протокол трехфазной
фиксации (3-Phase Commit). В нем вводится дополнительная фаза
precommit, на которой координатор сообщает всем узлам о принятом
решении фиксировать транзакцию. После этого, даже если произойдет
сбой координатора, оставшиеся узлы будут иметь право зафиксировать
транзакцию самостоятельно (а до фазы precommit — самостоятельно
отменить ее).
Однако оба алгоритма не справляются с ситуациями разделения сети.
14
Кворумные протоколы
Paxos, Raft, ZAB, E3PC и другие
при отказе до N/2–1 узлов связное большинство продолжает работу
меньшинство блокируется до устранения сбоя
большинство
(кворум)
Алгоритмы, основанные на кворуме (большинстве голосов), позволяют
в условиях разделения сети не блокировать узлы, сохранившие связь
друг с другом и образующие кворум. Таким образом (если голоса всех
узлов равны), из N узлов N/2+1 смогут продолжить работу.
Узлы, оставшиеся в меньшинстве, вынуждены ожидать исправления
сбоя, чтобы принять решение о фиксации или откате открытых
транзакций. Такие узлы могут полностью не обслуживать клиентов или
как минимум не принимать запросы на запись, чтобы не допустить
ситуацию split-brain.
Существует ряд алгоритмов, доказанных математически и имеющих
протестированные и проверенные временем реализации.
●
Paxos опубликован в 1998 году, а фактически появился в конце 80-х.
Есть целое семейство этих алгоритмов для различных ситуаций.
●
Raft создавался как алгоритм, эквивалентный Paxos, но более
простой для понимания. В этом алгоритме один из узлов выбирается
в качестве лидера, который может изменять данные; остальные узлы
читающие. Этот протокол лежит в основе таких систем, как etcd и
Consul (распределенные хранилища «ключ–значение»).
●
ZAB используется в системе ZooKeeper.
●
E3PC — Enhanced 3PC улучшает алгоритм трехфазной фиксации,
разрешая фиксировать транзакцию при получении большинства
(а не всех) голосов.
Существуют иные подходы, позволяющие решать проблему консенсуса,
например, Virtual synchrony (используется в Corosync).
15
Масштабируемость
Пропускная способность по чтению
распределение нагрузки по узлам кластера
Пропускная способность по записи
не достигается при полной репликации данных на все узлы
шардинг — распределение строк таблиц по разным узлам,
сильно зависит от природы хранимых данных
Время отклика
параллельные вычисления на нескольких узлах
Проще всего достичь масштабирования пропускной способности по
отношению к читающим транзакциям. Для этого достаточно каким-то
образом распределять читающие транзакции более или менее
равномерно по узлам кластера.
Достичь масштабирования пишущей нагрузки обычно сложнее:
в конфигурациях типа «мастер–мастер» узлы вынуждены обмениваться
друг с другом сообщениями и данными, так что общая нагрузка на
систему с увеличением числа узлов только возрастает. Однако это
возможно, если данные распределятся между узлами, а не
реплицируются на каждый узел полностью.
Обычно применяют шардинг: распределение данных на уровне строк
таблиц. Можно провести аналогию с секционированием, в котором
секции расположены на разных узлах кластера.
Заметим, что возможные схемы шардирования очень сильно связаны
с логикой данных; какое-то универсальное решение тут невозможно.
Использование шардинга позволяет достичь и масштабирования
времени отклика. Для этого кластер должен уметь распараллеливать
запросы, выполняя их одновременно на нескольких узлах, имеющих
необходимые данные. Это похоже на параллельное выполнение
запросов в стандартном PostgreSQL, но параллельные процессы
выполняются не внутри одного сервера, а на разных узлах. Конечно,
при этом возрастает стоимость и пересылки данных (например,
результатов частичной агрегации) между процессами.
16
Распределение нагрузки
Средства, специфичные для PostgreSQL
библиотека libpq
PgBouncer — менеджер пула
Pgpool-II — балансировщик и менеджер пула
Одиссей — многопоточный менеджер пула
…
Другие средства
виртуальный IP-адрес
HAProxy — балансировщик TCP/HTTP
round-robin DNS
…
Для распределения нагрузки уже имеется достаточно много готовых
решений; обычно такие средства не входят в состав самого кластера.
●
В строке подключения libpq можно указать несколько узлов, порядок
подключения к которым устанавливает параметр load_balance_hosts,
а параметром target_session_attrs задают свойства сеанса
(например, разрешение только читающих запросов).
●
PgBouncer — удобный менеджер пула соединений для PostgreSQL.
●
Pgpool-II — менеджер пула и балансировщик, способный
распределять пишущую нагрузку на мастер, а читающую на реплики.
●
Одиссей — многопоточный менеджер пула с полной поддержкой
SSL/TLS и возможностями аутентификации LDAP и PAM.
Из общих средств, не связанных с PostgreSQL, отметим:
●
Виртуальный IP-адрес — IP-адрес, который управляется
автоматикой кластера и направляет клиента на нужный узел.
●
HAProxy — балансировщик и прокси-сервер, работающий на уровне
TCP и HTTP. Для обновления списка узлов подходит средство
управления конфигурациями confd. Или можно использовать
способность HAProxy выполнять проверки (health check) узлов
кластера для исключения неподходящих из списка балансировки.
●
Некоторые системы (Consul), предоставляют round-robin DNS: выбор
узлов и балансировка может происходить на этапе разрешения
имени (но кеширование сводит на нет преимущества такого
решения).
Есть и другие средства. Многие можно использовать совместно.
17
Компоненты и примеры
Необходимые компоненты, отсутствующие в PostgreSQL
Примеры систем
18
Отказоустойчивость
Агент
управляет одним экземпляром PostgreSQL
Управляющий
логика управления всем кластером
Консенсус
внешняя система под управлением одного из протоколов консенсуса
поддержка общего состояния для управляющих
Точка входа
способ направить клиентов на нужный узел кластера
Если основной целью создания кластера является высокая доступность
(в частности, отказоустойчивость), естественным решением является
взять несколько экземпляров обычного PostgreSQL и объединить их
дополнительным программным обеспечением. Синхронизация данных
возлагается на физическую репликацию, то есть в кластере выделяется
основной узел (мастер) и реплики.
Поскольку PostgreSQL не может сам себя конфигурировать,
перезапускать, останавливать и т. п., нужна программа, которая будет
этим заниматься. Эту роль выполняет агент.
Логика управления кластером сосредоточена в управляющих. На этом
уровне принимается решение о том, что такой-то узел следует считать
вышедшим из строя, о том, какую из реплик следует назначить новым
мастером при выходе старого из строя и т. п.
Чтобы разделять общую точку зрения, управляющие опираются на
систему, работающую по одному из рассмотренных ранее
распределенных протоколов консенсуса.
Наконец, для клиентов требуется единая точка входа, которая
направит их на нужный узел (или узлы) кластера.
В разных кластерных системах эти компоненты могут называться по-
разному; некоторые могут объединяться вместе. Если какие-то
компоненты отсутствуют, кластер надо дополнять другими решениями.
19
Ограждение (fencing)
Отключение узла в неизвестном состоянии от клиентов
из-за разделения сети может возникнуть ситуация split-brain
нужно не только оградить узел,
но и не допустить в это время переход на реплику
Механизмы
отключение питания
отключение сетевого порта
завершение и перенаправление клиентских соединений
Поскольку в этом подходе серверы PostgreSQL ничего не знают о том,
что работают в кластере, важно уметь отгородить сбойный узел —
возможно, работающий, но потерявший связь с кластером — от
внешнего мира. Эта процедура называется fencing.
Если этого не сделать, то мастер, оказавшись в результате разделения
сети в меньшинстве, может продолжить работу (ситуация split-brain).
Полагаться на то, что управляющие, обнаружив сбой, сами остановят
сервер PostgreSQL, нельзя: у управляющих может не оказаться связи
с агентом, может произойти сбой в агенте и т. п.
Процедура может выполняться по-разному, но не должна рассчитывать
на доступность узла по сети. Например:
●
программно отключать питание сервера (интерфейсы IPMI, PDU);
●
программно отключать сетевой порт на коммутаторе;
●
если соединение идет через специализированный компонент, то
клиенты могут принудительно отключаться от сбойного узла на
уровне этого компонента.
Надо заметить, что процедура ограждения уменьшает масштаб
проблемы, но сама по себе не решает ее полностью. Нужно также
гарантировать невозможность перехода на одну из реплик до тех пор,
пока сбойный мастер не будет огражден. Ведь на принятие решения
об ограждении требуется время, в течение которого узел может
продолжать функционирование, и, следовательно, часть
зафиксированных данных может потеряться при восстановлении
(нулевое RPO не достигается).
20
Пример: Patroni
Raft / ZAB
etcd / consul / zookeeper
консенсус
агент
+ управляющий
точка входа
M
HAProxy
c
o
n
f
d
watchdog timer
Patroni https://github.com/patroni/patroni — кластерное решение
с открытым исходным кодом, рассчитанное на использование как
в облаках, так и на выделенных серверах.
Здесь агент и управляющий объединены в одном компоненте. Для
консенсуса используется хранилище «ключ–значение»,
предоставляемое одной из систем etcd (Raft), Consul (Raft) или
ZooKeeper (ZAB).
Для точки входа требуется использовать отдельное решение. Обычно
предлагается использовать HAProxy в связке с confd, который
периодически обновляет конфигурацию HAProxy на основе
информации из слоя консенсуса. Стандартная настройка предполагает
перенаправление всех клиентов на узел, выполняющий роль мастера.
Чтобы HAProxy сам не стал точкой отказа, необходимо иметь несколько
таких серверов и, например, виртуальный IP-адрес для клиентов.
Однако это приводит к тому, что конфигурации разных серверов
HAProxy нужно обновлять синхронно, и мы снова сталкиваемся
с вопросом построения кластера — уже на другом уровне.
Для ограждения узла (fencing) может использоваться механизм
сторожевого таймера (watchdog timer) ядра Linux. Если узел, будучи
мастером, не может связаться со слоем консенсуса в течение
определенного времени, таймер перезагружает операционную систему.
21
Пример: стек ClusterLabs
res. agentres. agentres. agent
virtual
synchrony
pacemaker
pacemaker
pacemaker
corosync
консенсус
управляющий
агент
точка входа
VIP
M
STONITH
Компания ClusterLabs (https://clusterlabs.org/) предоставляет стек
компонентов с открытым исходным кодом для построения кластеров из
более или менее произвольных систем.
Агент (resource agent в терминологии этой системы) представляет
конкретную систему как унифицированный «ресурс», реализуя
операции останова, запуска, повышения реплики и т. п. Этот компонент
должен быть реализован отдельно для каждой системы.
Управляющий компонент — Pacemaker — взаимодействует с ресурсом
через агента. Для общего состояния и обнаружения сбоев используется
Corosync (Virtual synchrony).
Ограждению (fencing) узлов уделено большое внимание. Используется
технология STONITH (Shoot The Other Node In The Head), позволяющая
надежно изолировать сбойный узел от кластера и клиентов путем
программного отключения питания или сетевого порта на коммутаторе.
Точкой входа для клиентов обычно является виртуальный IP-адрес,
который управляющий поднимает на узле, являющимся в данный
момент мастером.
Дополнительно при необходимости можно распределить читающую
нагрузку по репликам с помощью HAProxy, PgBouncer и т. п.
Для PostgreSQL имеется открытое решение PAF (PostgreSQL Automatic
22
Пример: BiHA
PostgreSQL
с модификацией
точка входа —
средства клиента
чтение
и запись
только
чтение
raft
M
узел-рефери
Очевидно, что большое количество внешних компонентов сильно
усложняет развертывание и обслуживание кластера. Чтобы упростить
эти задачи, можно встроить все необходимое внутрь самого
PostgreSQL. Эта нетривиальная задача решена в кластере BiHA (Built-in
High Availability), который является составной частью Postgres Pro
Enterprise.
Внешне такой кластер выглядит, как обычные узлы, связанные
физической репликацией, но на самом деле в каждом узле Postgres Pro
Enterprise реализован механизм обнаружения ошибок, предотвращения
разделения кластера (split-brain), слой консенсуса (используется
протокол Raft), логика управления и агент.
Для консенсуса нужно как минимум три узла, но не обязательно
хранить данные на всех трех. Можно развернуть кластер, в котором
третий узел будет являться рефери: он будет входить в кворум, но не
будет участвовать в репликации. Для такого узла можно использовать
менее мощный сервер.
Дополнительное программное обеспечение для точки входа не
используется, кластер полагается на способность клиента
самостоятельно определять, работает ли он с основным сервером
(выполняющим пишущие и читающие запросы) или с репликой. Такие
средства имеются в том числе в libpq.
23
Согласованность
Менеджер распределенных транзакций
требует модификации ядра PostgreSQL
может быть встроен или представлен отдельным компонентом
В предыдущих примерах к PostgreSQL добавлялись компоненты,
которые позволяют обеспечить отказоустойчивость: агенты,
управляющие (включая систему обнаружения сбоев), слой консенсуса
для хранения состояния кластера. Эти компоненты могли существовать
как снаружи узлов PostgreSQL, так и быть встроенными в сам
PostgreSQL.
Для создания кластера, предоставляющего клиентам единую
согласованную картину данных, необходим менеджер распределенных
транзакций (Global Transaction Manager), который реализует протокол
распределенного консенсуса для атомарной фиксации транзакций,
охватывающих несколько узлов.
Возможность работы с распределенными транзакциями нельзя
организовать исключительно внешними средствами, она должна быть
встроена в PostgreSQL путем модификации ядра. Даже в том случае,
когда управление распределенными транзакциями выделено
в виде самостоятельного компонента кластера, PostgreSQL должен
понимать, что к этому компоненту необходимо обращаться.
Обычно помимо согласованности требуется и отказоустойчивость.
В этом случае кластер должен содержать и все рассмотренные ранее
компоненты. А если менеджер распределенных транзакций реализован
в виде отдельного компонента, то необходимо обеспечить и его
отказоустойчивость, что приводит к очередному усложнению системы.
24
Пример: multimaster
E3PC,
paxos
PostgreSQL
точка входа
логическая
репликация
HAProxy
узел-рефери
Примером такой системы может служить расширение multimaster
Postgres Pro Enterprise. В СУБД встраивается менеджер
распределенных транзакций, который расширяет имеющийся
двухфазный протокол 2PC до E3PC. Для этого на узлах запускаются
процессы-арбитры, выполняющие дополнительные коммуникации.
Используется алгоритм консенсуса Paxos.
Любой из узлов кластера multimaster (кроме рефери, если он
используется) может принимать запросы как на чтение, так и на запись.
Читающие транзакции выполняются локально. Пишущие транзакции
являются распределенными; узел, фиксирующий транзакцию,
становится координатором.
Репликация данных между узлами кластера основана на логической
репликации, DDL реплицируется собственным алгоритмом.
Для распределения нагрузки имеет смысл балансировать соединения
между узлами кластера. Например, можно указывать разные строки
соединений для разных клиентов, а можно воспользоваться HAProxy.
Приложение, работающее на одном сервере, может быть запущено и
на multimaster-кластере без дополнительных забот о согласованности
данных. Поддерживаются уровни изоляции Read Committed и
Repeatable Read. Однако, в силу ограничений реализации, некоторые
нюансы приложение должно учитывать.
Помимо реализации распределенных транзакций, кластер
обеспечивает и отказоустойчивость.
25
Масштабируемость
Большое количество узлов
репликация или общий диск
шардинг
параллельные вычисления
В случаях, когда на первый план выходят возможности
масштабирования, можно организовать относительно простой кластер,
составленный из обычных экземпляров PostgreSQL без обычной
кластерной обвязки. Такой кластер позволит распределять нагрузку на
разные узлы, хотя и не будет обеспечивать отказоустойчивость и
согласованность.
Данные между узлами могут реплицироваться полностью (получим
возможность масштабирования по чтению) или частично (например,
по принципу шардинга — получим возможность масштабирования и по
записи). Системы, рассчитанные на облачную инфраструктуру и
динамическую подстройку ресурсов под нагрузку, могут использовать
вариант с общим диском и разделением узлов на вычислительные и
хранящие данные.
Распределение данных по большому количеству узлов позволяет
организовать и параллельное выполнение запросов.
26
Пример: Citus
2PC
PostgreSQL
точка входа
координатор
рабочий узелрабочий узел
шардинг
и параллельное
выполнение
Citus https://github.com/citusdata/citus — расширение PostgreSQL для
распределения данных и запросов по узлам кластера.
Citus — пример системы, в которой данные не реплицируются
полностью, а шардируются на рабочие узлы. Один узел выделяется
в качестве координатора — он содержит метаинформацию
о расположении данных.
В состав расширения входит специальный оптимизатор запросов,
генерирующий параллельные планы выполнения, а исполнитель
выполняет запросы, распределяя их на соответствующие узлы. За счет
этого кластер Citus позволяет достичь масштабируемости по времени
отклика и масштабируемости пишущей нагрузки.
Citus гарантирует атомарность изменений на всех узлах кластера
с помощью глобальных транзакций по протоколу двухфазной фиксации
2PC.
Перебалансировка данных между рабочими узлами (при добавлении
нового узла или выведении старого) использует логическую
репликацию.
В представленном виде кластер не решает задачу высокой
доступности: при отказе любого узла теряется часть информации.
Поэтому и координатор, и рабочие узлы могут заменяться отдельными
мини-кластерами из трех узлов (мастер и две реплики), которые и
обеспечат отказоустойчивость.
27
Пример: Neon
safekeeper pageserver
S3
WAL
paxos
compute
storage
R/W
страницы данных
Решение с открытым кодом Neon продвигает концепцию облачных
бессерверных вычислений. Идея заключается в разделении СУБД на
отдельные узлы, не требующие хранения состояния, и использовании
большого и дешевого, но медленного хранилища данных.
Вычислительные узлы (по сути, экземпляры PostgreSQL, от которого
«отрезано» журналирование и работа с диском) запускаются
в контейнерах под управлением Kubernetes. Поскольку эти узлы
не имеют собственного состояния, контейнеры можно запускать и
останавливать в любой момент в зависимости от активности клиентов,
не платя за использование лишних ресурсов.
Для сохранения данных пишущий экземпляр (он может быть только
один) передает журнальные записи компоненту safekeeper.
Журнальные записи зафиксированных транзакций необходимо хранить
надежно; для этого компоненты safekeeper образуют отдельный
отказоустойчивый кластер, работающий по проколу Paxos.
Журнальные записи асинхронно передаются от safekeeper компоненту
pageserver, который применяет их к страницам данных. Pageserver
возвращает страницы по запросу клиентов, при необходимости
сбрасывая их в «бездонное» объектное хранилище S3 и считывая их
оттуда. Таким образом, pageserver служит кешем данных, с которыми
ведется активная работа, и является аналогом буферного кеша.
Neon масштабируется по чтению и автоматически адаптируется
к нагрузке, но неизбежно работает медленнее традиционных систем.
28
Итоги
Для создания кластера требуются средства,
не входящие в стандартный PostgreSQL
Огромное количество вариантов построения кластера
разные цели: высокая доступность, масштабируемость, согласованность
решения с внешними компонентами и с модификацией ядра
решения с поддержкой глобальных транзакций
Чем выше предъявляются требования,
тем сложнее система и тем дороже она обходится