Управление ресурсами СУБД =========================== .. |br| raw:: html
Ресурсные группы перекладывают разграничение ресурсов на ядро ОС **Linux** с его механизмом *cgroups*. По факту, SQL-команды управления ресурсными группами являются обертками над виртуальной файловой системой */sys/fs/cgroup* хостов кластера **ADB**. Из всех возможных ресурсов **ADB** умеет управлять через ресурсные группы только памятью и процессором с помощью подкаталогов *cgroups*: memory, cpu и cpuset. При этом ресурсы процессора можно распределять как по полосе загрузки в процентах (cpu), так и по ядрам между различными группами (cpuset). Логика управления ресурсами напоминает конструкцию из ящиков, вложенных друг в друга (:numref:`Рис.%s.`). .. _intro_resources_logic: .. figure:: ../imgs/intro_resources_logic.* :align: center Логика управления ресурсами Управления памятью --------------------- Управление памятью делится по уровням разграничения. Первый уровень разграничения ресурсов памяти определяется параметром *gp_resource_group_memory_limit*. Параметр указывает ядру ОС, сколько процентов памяти на каждом хосте принадлежит запущенным процессам **ADB**. Указанная память делится поровну между всеми сегментами на хосте. На рисунке в блоке "Segment Host Memory" показана вся память хоста, где зеленым отмечена память под **ADB**, поровну разделенная между четырьмя сегментами. .. important:: При установке значения параметра важно оставить память для самой ОС. При этом процессы ADB не могут получить больше памяти, чем задано. Так же никакие другие внешние процессы не имеют возможности забрать память из зарезервированной для ADB Следующий уровень вложенности -- память, выделенная отдельному сегменту. В "ящик" памяти блока "Segment N" на рисунке вкладываются "ящики" поменьше, каждый из которых является отдельной ресурсной группой. На картинке приведено три ресурсные группы -- *admin_group*, *default_group* и *rg_sample*. .. important:: Процент памяти ресурсной группы задается параметром *memory_limit*, который всегда меньше или равен *100%*. Сумма *memory_limit* по всем ресурсным группам так же не должна превышать *100%* (в ином случае ADB выдает ошибку) Параметр *memory_limit* гарантирует, что ресурсная группа получает свой процент памяти. В случае если сумма параметров *memory_limit* по всем ресурсным группам менее *100%* (т.е. в наличии имеется свободная память), то в момент выполнения тяжелых запросов любая ресурсная группа может позаимствовать данную свободную память. В примере на картинке ресурсной группе *rg_sample* выделено *60%* памяти от доступной сегменту, у которого *25%* памяти (так как память делится поровну между сегментами) от *gp_resource_group_memory_limit*, составляющей *80%* от всей памяти хоста (5 равных секций в блоке "Segment Host Memory"). Для предотвращения активной утилизации памяти тяжелыми запросами можно указать параметр *memory_spill_ratio*, который не позволяет запросу использовать более указанного процента памяти от доступного ресурсной группе. В результате, если планировщик видит, что запрос должен потребить более *memory_spill_ratio* процентов памяти в ресурсной группе, то его данные сбрасываются на диск. При этом статистика должна быть актуальной во избежание ошибки планировщика (иначе запрос отменяется из-за нехватки памяти). Интересная особенность в том, что *memory_spill_ratio* можно менять внутри сессии. Управление ЦПУ ------------------ Существует два взаимодополняющих друг друга сценария управления ресурсами ЦПУ через ресурсные группы: по ядрам или по полосе пропускания. Если обрабатываются долгие тяжелые запросы в ресурсной группе малым количеством одновременных потоков, а в других ресурсных группах выполняется много более легковесных запросов, то на ядрах процессора, обслуживающих тяжелые запросы, происходят частые переключения контекста. Это сильно замедляет производительность и для такой ресурсной группы можно зарезервировать определенные ядра ЦПУ с помощью *cpuset*. В результате только данная ресурсная группа использует указанные ядра (их номера должны присутствовать на всех хостах). Для остальных ресурсных групп данные ядра недоступны даже в случае их простаивания -- поэтому следует резервировать ядра только в крайних случаях и в минимальном количестве. Так же следует понимать, что планировщик ОС может использовать ядра и для других программ, а не только для СУБД **ADB**. Возможно выделять полосу загрузки ЦПУ. Существует параметр *gp_resource_group_cpu_limit*, указывающий максимальный процент ресурсов ЦПУ, который может потребить **ADB** со всеми ресурсными группами внутри. Он используется в первую очередь для того, чтобы защитить саму ОС и другие программы на хосте при высоких нагрузках **ADB**. .. important:: Рекомендуется не выставлять параметр *gp_resource_group_cpu_limit* выше значения *0.9* (*90%*) Для управления полосой загрузки ЦПУ для ресурсной группы используется параметр *cpu_rate_limit*. При этом сумма *cpu_rate_limit* по всем ресурсным группам не должна превышать *100%*. Если в других ресурсных группах нет выделенных ядер (*cpuset*), то *cpu_rate_limit* показывает доступный данной ресурсной группе процент от присущих базе данных ресурсов ЦПУ на хосте (*gp_resource_group_cpu_limit*). В случае если для части ресурсных групп используются выделенные ядра, то доступные ресурсы определяются как минимальное из двух значений: + Незарезервированные ядра ЦПУ / все ядра ЦПУ; + *gp_resource_group_cpu_limit*. Пример ---------- Для примера используется профиль нагрузки на кластер, который единовременно загружен: + 50 "легкими" запросами SQL1, извлекающими единственную строку по первичному ключу; + 20 "средними" запросами SQL2, содержащими 2-3 соединения и возвращающими несколько десятков строк; + 2 "тяжелыми" запросам SQL3, считающими витрины со множеством соединений и по большим объемам данных (минимизация времени выполнения критична); + 1 "сверхтяжелый" запрос SQL4, загружающий данные из ETL системы через *gpfdist*. Тогда можно использовать разделение ресурсов в кластере, представленное в таблице. .. csv-table:: Пример разделения ресурсов в кластере :header: "Ресурсная |br| группа", "Запросы", "concurrency", "cpu |br| rate |br| limit", "cpuset", "memory |br| limit", "memory |br| shared |br| quota", "memory |br| spill ratio" :widths: 20, 20, 10, 10, 10, 10, 10, 10 "g1", "SQL1", "50", "35", " -- ", "20", "20", "20" "g2", "SQL2", "20", "25", " -- ", "15", "10", "20" "g3", "SQL3", "2", " -- ", "1-16", "40", "0", "40" "g4", "SQL4", "1", "30", " -- ", "15", "0", "20" "default_group", " -- ", "20", "5", " -- ", "5", "50", "20" "admin_group", " -- ", "10", "5", " -- ", "5", "50", "20" Для "тяжелых" запросов SQL3 с перестройкой витрин используются выделенные ядра *1-16* на каждом хосте (из *72* физических в наличии), что минимизирует переключения контекста на них и сильно увеличивает скорость запроса под высококонкурентной нагрузкой. Для "сверхтяжелого" запроса SQL4 нет необходимости в выделенных ядрах, так как при загрузке данных из ETL переключение контекста не оказывает сильного влияния на производительность. А правильная стратегия с выделенными ядрами под ресурсную группу заключается в использовании данного решения только тогда, когда не остается других вариантов (выделенные ядра ЦПУ "невидимы" для остальных ресурсных групп).