Боремся с нагрузками в HPE Vertica

Типовой сценарий работы «just in time» хранилища данных выглядит так: десятки (ETL) сессий почти непрерывно захватывают с источников данные и вставляют их в хранилище. Параллельно множество других (ELT) сессий отслеживают поступление данных, заполняют консолидированный слой и ведут расчет агрегатов и витрин. Одновременно с этим, на поступающих первичных и рассчитанных данных, выполняют запросы пользователи, BI и другие системы. Вся эта каша должна ладно вариться в рамках сервера хранилищ данных, без тормозов и затыков, какими бы не были пиковые нагрузки.

В HPE Vertica для планирования работы сервера под нагрузками разработан специальный механизм, под названием «ресурсные пулы». Идея его в том, что каждый пользователь сервера работает в рамках выделенного ресурсного пула, который регулирует приоритетность доступа к ресурсам кластера, ограничивает конкурентность выполнения запросов и описывает правила резервирования и работы с памятью сервера.

По умолчанию после установки сервера Vertica на созданной базе данных это выглядит примерно так:

d1184cf6ce944de1b88778b455a5857e.jpg


На каждом сервере (ноде) кластера доступная для Vertica память обозначается, как ресурсный пул General. Для различных нужд сервера автоматически создаются служебные пулы, которые отрезают от General для себя кусочки памяти:
  • WOS — для хранения в области памяти порционно вставляемых данных
  • TM — для фонового служебного процесса Tuple Mover, выполняющего moveoute и mergeout операции
  • Прочие служебные — Vertica имеет дополнительные ресурсные пулы для проведения операций восстановления нод кластера, построения проекций, выполнения служебных запросов и т.д.

По умолчанию пользователи сервера крутятся под управлением самого пула General. Можно посмотреть его текущие настройки:
dbadmin=> SELECT * FROM resource_pools WHERE name = 'general';
-[ RECORD 1 ]------------+------------------
pool_id                  | 45035996273721212
name                     | general
is_internal              | t
memorysize               |
maxmemorysize            | 30G
executionparallelism     | AUTO
priority                 | 0
runtimepriority          | MEDIUM
runtimeprioritythreshold | 2
queuetimeout             | 00:05
plannedconcurrency       | 10
maxconcurrency           | 20
runtimecap               |
singleinitiator          | f
cpuaffinityset           |
cpuaffinitymode          | ANY
cascadeto                |

Расшифрую некоторые параметры:
  • memorysize — размер зарезервированной памяти пула, этот размер отрезается от пула General, уменьшая его размер доступной памяти, для General соответственно не указывается.
  • maxmemorysize — максимально разрешенный размер использования памяти пулом. В случае использования пулом памяти, более указанной memorysize, недостающая память временно забирается из пула General, если там есть конечно что забирать.
  • plannedconcurrency — делитель для установки размера памяти, выделяемой сессии при старте запроса в пуле, в данном случае для пула General maxmemorysize: 30 гб делить 10 plannedconcurrency = каждой сессии при старте выделяется 3 гб памяти пула, что мягко говоря жирновато.
  • maxconcurrency — максимальное количество выполняемых на пуле конкурентных сессий.

Такие «дефолтные» настройки дают фактически 100% гарантию тормозов работы Vertica:
  • 10 сессий запускает выполнение запросов
  • каждой сессии выделяется по 3 гб памяти пула, даже если столько и не нужно
  • Если сессии еще не успели отработать и новые сессии запустили еще запросы, то вместо памяти из 30 гб они получают работу в свапе
  • Если какая-то сессия в ходе работ съела всю память пула, то другие сессии так же оказываются в свапе

Давайте поможем «горю»:
ALTER RESOURCE POOL general PLANNEDCONCURRENCY 20 MAXCONCURRENCY 10;

Теперь каждой сессии при старте запроса в пуле выделяется 0.5 гб, а всего одновременно может выполнятся не более 10 сессий. При старте 10 сессий будет израсходовано 5 гб памяти пула, еще 25 гб останется как резерв для отдачи памяти тяжелым запросам и другим ресурсным пулам.

Хочу обратить внимание на параметр MAXCONCURRENCY — чем он ниже, тем быстрее будут работать ваши запросы. У каждой аппаратной части есть предел нагрузок, при превышении которого все «встает колом». Чем выше конкурентность, тем больше нагрузка на процессоры и дисковые массивы, тем ниже их скорость работы. Эффективнее выполнить 10 запросов и далее с очереди выполнить следующие 10 запросов, чем пытаться выполнить одновременно 20 запросов. Естественно, MAXCONCURRENCY будет в первую очередь зависеть от поставленных для решения задач пула и от характеристик железа кластера, Ваша задача выявить пределы и выставить его чуть ниже пределов, чтобы в случае пиковых нагрузок одновременного выполнения множества тяжелых запросов, кластер вдруг не затормозил для всех его пользователей.

Так что же с пулами? Пока мы только настроили General, однако держать в нем пользователей на самом деле дурная практика. Давайте сделаем типовые пулы по группам задач пользователей:

-- Пул для писателей
CREATE RESOURCE POOL writers
MEMORYSIZE '2G'
MAXMEMORYSIZE '10G'
PLANNEDCONCURRENCY 10
MAXCONCURRENCY 10
PRIORITY -50
RUNTIMECAP '3 MINUTE'
RUNTIMEPRIORITYTHRESHOLD 0;

-- Пул для тяжелых длинных запросов
CREATE RESOURCE POOL slowly
MEMORYSIZE '0%'
MAXMEMORYSIZE '20G'
MAXCONCURRENCY 7
RUNTIMEPRIORITY LOW
QUEUETIMEOUT '15 MINUTE'
RUNTIMEPRIORITYTHRESHOLD 0;

--  Пул для читателей
CREATE RESOURCE POOL readers
MEMORYSIZE '4G'
MAXMEMORYSIZE '10G'
PLANNEDCONCURRENCY 20
MAXCONCURRENCY 10
RUNTIMECAP '5 MINUTE'
PRIORITY 50
RUNTIMEPRIORITYTHRESHOLD 3
CASCADE TO slowly;

General теперь у нас «похудел»:
999a80f890bf42169e09217da91085b8.jpg

Что мы сделали:
  1. Раскидали по группам сессии пищушие, читающие и выполняющие длинные тяжелые запросы.
  2. Выделили начальную память пулам читателей и писателей, чтобы для большинства коротких запросов память бралась сразу из резервированной для пула без подкачки с General.
  3. Не стали выделять память пулу тяжелых запросов, все равно его сессии захотят много памяти и резервировать для них, отрезая драгоценную память из General, смысла нет, зато этому пулу разрешили забирать до 20 гб памяти от General в случае необходимости.
  4. Выставили для пула тяжелых запросов пониженный приоритет выполнения LOW.
  5. Для пула писателей и читателей выставили приоритет доступа к ресурсам так, чтобы по приоритету выполнения MEDIUM пул писателей был аутсайдером (-50 приоритет), пул General был посередине (нулевой приоритет) и пул для читателей был выше этих пулов (50 приоритет).
  6. Рационально выставили значения конкурентности пулам, где одновременно могут выполняться 10 запросов писателей, 10 запросов читателей и 7 тяжелых запросов.
  7. Для ситуаций, когда пользователь запустил тяжелый запрос в пуле читателей, который длиться более 5 минут, указали перевод каскадом таких запросов на пул тяжелых запросов. Это гарантирует, что пул читателей не просядет при выполнении на нем длинных запросов, которые забьют слоты выполнения конкурентов и замедлят конвеер выполнения из очереди быстрых запросов. Для пула писателей выставили ограничения выполнения запросов не более 3 минут, чтобы неоптимизированные запросы вставки или обновления данных сшибались.
  8. Для пула тяжелых запросов выставили время ожидания в очереди 15 минут. Если все конкурирующие слоты у пула заняты, то после 15 минут ожидания, стоящие в очереди запросы будут прекращены с ошибкой. Это даст понять пользователям, что сервер не висит, а просто в рамках их пула все занято.
  9. Для пула читателей выставили время в 3 секунды, в течении которых запрос после запуска имеет наивысший приоритет по ресурсам. Это позволит быстро выполнять короткие запросы, освобождая место в пуле для других запросов

Теперь назначаем пользователям нужные пулы и дело сделано:
ALTER USER user_writer RESOURCE POOL writers;
ALTER USER user_reader RESOURCE POOL readers;
ALTER USER user_analytical RESOURCE POOL slowly RUNTIMECAP '1 HOUR' TEMPSPACECAP '10G';

Здесь мы дополнительно, помимо пула, пользователю «user_analytical» ограничили для запросов время выполнения одним часом и разрешили использовать не более 10 гб пространства в TEMP.

Хочу заметить, что все вышеописанное в статье является действием «куда копать», а не примером «что настроить». Сколько и каких будет ресурсных пулов, с какими характеристиками — это все должны решить Вы сами. Можно начать с малого — создать по примеру подобные 3 пула и дальше смотреть по нагрузкам кластера, постепенно балансируя параметры пулов и выделяя группы пользователей по разным пулам. Главное помнить, что:

  1. Пул General является общим кладезем памяти для всех пулов и лучше его не использовать напрямую для работы пользовательских сессий
  2. Чем меньше конкурентов, тем меньше скорость проседания железа в пиковых нагрузках
  3. Сумма максимально разрешенной памяти всех пулов не должна перекрывать память General

Комментарии (0)

© Habrahabr.ru