В этой статье мы хотим вынести на обсуждение сообщества вопрос: что означает полноценно реализовать в Greenplum возможность уменьшения числа сегментов и/или хостов?
Наш базовый тезис таков: возможность масштабирования кластера как в меньшую, так и большую сторону реализована полноценно в решении только тогда, когда кластер приводится в состояние сбалансированного (или остаётся таковым) как после выполнения операции shrink, так и expand.
Отличие этой статьи от предыдущих (например, отличная статья Романа Ескина по выделению в Greengage планировщика ORCA в расширение) в том, что работа на момент её написания в самом разгаре. На текущем этапе обсуждаются общие границы решения, основные тезисы и требования, которые неизбежно могут претерпеть некоторые изменения по мере продвижения.
В чем же сложность сократить количество сегментов или хостов: вроде как, ломать — не строить?
Как известно, сегменты в Greenplum нумеруются по content
(номер сегмента, одинаковый для пары primary-mirror). В данном случае на Рис. 1 значение content
представляет собой постфикс после идентификатора seg
, цветом обозначены primary- и mirror-сегменты, тип зеркалирования — group
.
Когда кластер расширяется gpexpand
, то в кластер добавляются новые сегменты (content
и dbid
инкрементируются и присваиваются новым сегментам). При этом gpexpand
в интерактивном режиме не даст нарушить доступные схемы зеркалирования, в том числе накладывая требования по количеству новых хостов. Для сохранения стратегии зеркалирования gpexpand
запрашивает добавление двух и более хостов (на Рис. 2 они выделены пунктиром).
В режиме передачи в gpexpand
файла конфигурации расширения кластера (опция --i
) допускается гораздо большая свобода действий, но все они остаются на совести пользователя.
Если бы утилита была более продвинутой с точки зрения сохранения конфигурации зеркалирования, то можно было бы добиться сбалансированного расширения, довольствуясь одним хостом. Это бы потребовало ребаланса, например, группы [seg6
, seg7
, seg8
] c sdw1
на новый хост sdw4
, в то время как на хост sdw1
приехала бы как раз новая введенная в кластер группа [seg9
, seg10
, seg11
].
Это является ограничивающим фактором использования интерактивного режима gpexpand
. Однако, сформировав файл конфигурации вручную, равномерность распределения можно скорректировать (или сломать), например, такой утилитой, как gpmovemirrors
.
Чтобы изменить политику редистрибуции таблицы на изменившееся число сегментов, необходимо учесть новое количество сегментов.
Вычисленный хеш для строки (если точнее, то подсчитанный для ключей распределения данной таблицы) хранится в поле hash
структуры CdbHash
, там же хранится и число сегментов numsegs
:
typedef struct CdbHash
{
uint32 hash; /* The result hash value */
int numsegs; /* number of segments in Greenplum Database used for partitioning */
CdbHashReduce reducealg; /* the algorithm used for reducing to buckets */
bool is_legacy_hash;
int natts;
FmgrInfo *hashfuncs;
} CdbHash;
Чтобы получить итоговое значение хеша, которое определит сегмент-источник/приёмник для данной строки, происходит финальное схлопывание (reduce) хеша до нужного значения функцией cdbhashreduce. Эта функция отвечает за вычисление номера целевого сегмента. Начиная с версии 6 вычисление номера базируется на алгоритме jump consistent hash.
Данный алгоритм требует использования непрерывной последовательности идентификаторов "бакетов" (в терминах общего описания алгоритма), в нашем случае сегментов (0, num_segments)
. Таким образом, основным ограничением использования данного алгоритма является добавление или удаление начиная со "старших" сегментов. Выкинуть из кластера сегмент где-то "посередине" в такой картине мира нельзя.
Всё бы хорошо и гладко, но размещены эти старшие сегменты могут быть на любых хостах. Чем это может в итоге завершиться? Несбалансированным кластером (Рис. 4).
По итогу на sdw1
остаются 3 primary-сегмента, на sdw3
уже 3 mirror-сегмента, на sdw2 пара групп по 3 сегмента. Выглядит не очень сбалансировано. Хост sdw3
остаётся только на применение WAL-записей от sdw2
.
При этом приводить к сбалансированному виду DBA придётся вручную. Вряд ли те из них, кто управляет большими кластерами, будут этому рады.
Обобщить задачу увеличения или сокращения числа сегментов таким образом, что главным процессом будет ребаланс сегментов на некоторое множество хостов и сегментов. Тем самым перед нами открываются возможности:
Сокращение числа сегментов без изменения числа хостов, если, например, администратор решил выделить аппаратные ресурсы под какие-то другие задачи, но менять число сегментов не потребовалось.
Сокращение и числа сегментов, и числа хостов. Например, с целью высвобождения хостов и передачи их в другой кластер.
Осуществление ребаланса ровно в той же конфигурации с точки зрения количества хостов и сегментов. По сути, приведение в сбалансированное состояние по какой-то причине ранее несбалансированной конфигурации с учетом стратегии зеркалирования.
Облегчение жизни пользователя за счет предоставления в его распоряжение одной утилиты, которая будет решать комлексную задачу масштабирования кластера, в частности, заменяя собой gpexpand
.
Для тех пользователей, которые захотят по каким-то причинам продолжить использовать имеющиеся утилиты, известный нам gpexpand
и его предполагаемый "антипод" — gpshrink
— можно уже рассматривать как обёртки над обобщенным решением в виде gprebalance
. Технически они будут использовать gprebalance
.
Есть еще пара, на наш взгляд, важных возможных улучшений.
Возможный вариант оптимизации шага перераспределения таблиц с "jump consistent-распределением" при сокращении числа сегментов — это отказ от подхода CREATE TABLE AS SELECT
и реализации переноса данных с выводимых сегментов через INSERT SELECT
. Действительно, так как из эксплуатации выводятся старшие сегменты (а это означает, что строки на существующих сегментах останутся, как есть), то можно получить, предположительно, весомый выигрыш времени на перераспределении только подмножества данных старших сегментов, а не полного пересоздания таблицы. Я думаю, что по мере продвижения работы по этой подзадаче мы поделимся с вами результатами сравнительных тестов производительности.
Второй момент, на который мы обратили внимание, что при перемещении сегментов реплика отключается почти сразу и может быть долгое время недоступна. Это не является большой проблемой при восстановлении сегментов или каких-то единичных перемещений, но для задачи полноценной и потенциально масштабной ребалансировки кластера это может оказаться критичным. Мы также работаем и в этом направлении.
Как итог, хочу привести список тезисов, которые можно считать основными требованиями к утилите, которую мы пока называем gprebalance
:
Утилита должна обеспечивать возможность приведения кластера в сбалансированное состояние.
Должны поддерживаться конфигурации зеркалирования group
или spread
.
В идеале утилита должна поддерживать балансировку точек монтирования, которые могут быть назначены пользователям для сегментов в качестве каталогов размещения данных. В противном случае пользователь может удивиться, что на одном узле сгруппировались все сегменты с определенной точкой монтирования.
Утилита должна поддерживать ребаланс на заданный список хостов.
Утилита должна поддерживать режим моделирования всех действий "в памяти", без реального перемещения (dry run).
Утилита должна иметь возможность представить пользователю план перемещений сегментов в виде списка действий по перемещению сегментов между узлами.
Утилита должна поддерживать режим отката проделанных изменений кластера.
Утилита должна поддерживать выполнение операций по переносу либо в заданный интервал времени, либо в заданную продолжительность.
При возникновении необходимости переключить роль primary → mirror или обратно утилита (сама или с помощью других утилит) может это сделать в автоматическом режиме, либо утилита должна прерывать своё выполнение и запрашивать явное подтверждение пользователя допустимости выполнения шага переключения ролей.
Утилита должна минимизировать время отсутствия доступного зеркала при выполнении операции перемещения сегментов.
Утилита должна поддерживать режим отображения информации о прогрессе выполнения ребаланса.
Утилита должна обрабатывать возможные ситуации failover-сегментов, выхода из строя узлов.