Перейти к основному содержимому
Перейти к основному содержимому

Планирование загрузок

Когда ClickHouse выполняет несколько запросов одновременно, они могут использовать общие ресурсы (например, диски). Ограничения планирования и политики могут быть применены для регулирования использования и совместного использования ресурсов между различными нагрузками. Для каждого ресурса можно настроить иерархию планирования. Корень иерархии представляет ресурс, в то время как листья — это очереди, хранящие запросы, которые превышают ёмкость ресурса.

примечание

В настоящее время планирование дискового ввода-вывода и CPU можно осуществлять с помощью описанного метода. Для гибких ограничений по памяти смотрите Переизбыток памяти

Конфигурация дисков

Чтобы включить планирование ввода-вывода для конкретного диска, необходимо создать ресурсы чтения и записи для доступа WRITE и READ:

Ресурс может быть использован для любого количества дисков для READ или WRITE или для обоих. Есть синтаксис, позволяющий использовать ресурс для всех дисков:

Альтернативный способ указать, какие диски используются ресурсом, — это конфигурация сервера storage_configuration:

осторожно

Планирование загрузок с использованием конфигурации ClickHouse устарело. Вместо этого следует использовать SQL-синтаксис.

Чтобы включить планирование ввода-вывода для конкретного диска, необходимо указать read_resource и/или write_resource в конфигурации хранилища. Это указывает ClickHouse, какой ресурс должен использоваться для каждого запроса чтения и записи с данным диском. Ресурс чтения и записи могут ссылаться на одно и то же имя ресурса, что полезно для локальных SSD или HDD. Несколько различных дисков также могут ссылаться на один и тот же ресурс, что удобно для удалённых дисков: если вы хотите обеспечить справедливое распределение пропускной способности сети между, например, производственными и развивающимися нагрузками.

Пример:

Обратите внимание, что параметры конфигурации сервера имеют приоритет над SQL-методом определения ресурсов.

Разметка загрузки

Запросы могут быть размечены с помощью настройки workload, чтобы различать различные нагрузки. Если workload не установлен, используется значение "default". Обратите внимание, что вы можете указать другое значение, используя профили настроек. Ограничения настройки могут быть использованы, чтобы сделать workload постоянным, если вы хотите, чтобы все запросы от пользователя были размечены фиксированным значением настройки workload.

Также возможно назначить настройку workload для фоновой активности. Слияния и мутации используют настройки сервера merge_workload и mutation_workload соответственно. Эти значения также могут быть переопределены для конкретных таблиц с помощью настроек merge_workload и mutation_workload для Merge Tree.

Рассмотрим пример системы с двумя разными нагрузками: "production" и "development".

Иерархия планирования ресурсов

С точки зрения подсистемы планирования ресурс представляет собой иерархию узлов планирования.

осторожно

Планирование загрузок с использованием конфигурации ClickHouse устарело. Вместо этого следует использовать SQL-синтаксис. SQL-синтаксис автоматически создает все необходимые узлы планирования, и следующее описание узлов планирования следует рассматривать как детали реализации нижнего уровня, доступные через таблицу system.scheduler.

Возможные типы узлов:

  • inflight_limit (ограничение) - блокирует, если число одновременных запросов в работе превышает max_requests, или их общая стоимость превышает max_cost; должен иметь единственного дочернего узла.
  • bandwidth_limit (ограничение) - блокирует, если текущая пропускная способность превышает max_speed (0 означает неограниченный) или всплеск превышает max_burst (по умолчанию равен max_speed); должен иметь единственного дочернего узла.
  • fair (политика) - выбирает следующий запрос для обслуживания из одного из своих дочерних узлов в соответствии с max-min справедливостью; дочерние узлы могут указывать weight (по умолчанию 1).
  • priority (политика) - выбирает следующий запрос для обслуживания из одного из своих дочерних узлов в соответствии со статическими приоритетами (меньшее значение означает более высокий приоритет); дочерние узлы могут указывать priority (по умолчанию 0).
  • fifo (очередь) - лист иерархии, способный удерживать запросы, которые превышают ёмкость ресурса.

Чтобы иметь возможность использовать полную ёмкость основного ресурса, вы должны использовать inflight_limit. Обратите внимание, что низкое значение max_requests или max_cost может привести к не полной загрузке ресурса, в то время как слишком высокие значения могут привести к пустым очередям внутри планировщика, что, в свою очередь, приведет к игнорированию политик (несправедливости или игнорированию приоритетов) в поддереве. С другой стороны, если вы хотите защитить ресурсы от чрезмерной загрузки, вы должны использовать bandwidth_limit. Оно ограничивает, если количество потребляемых ресурсов в течение duration секунд превышает max_burst + max_speed * duration байт. Два узла bandwidth_limit на одном ресурсе могут быть использованы для ограничения пикового времени пропускной способности в течение коротких интервалов и усредненной пропускной способности для более длительных.

Следующий пример показывает, как определить иерархии планирования ввода-вывода, показанные на рисунке:

Классификаторы загрузки

осторожно

Планирование загрузок с использованием конфигурации ClickHouse устарело. Вместо этого следует использовать SQL-синтаксис. Классификаторы создаются автоматически при использовании SQL-синтаксиса.

Классификаторы загрузок используются для определения отображения из workload, указанного в запросе, в очереди-листья, которые должны использоваться для конкретных ресурсов. В данный момент классификация нагрузки проста: доступно только статическое отображение.

Пример:

Иерархия загрузок

ClickHouse предоставляет удобный SQL-синтаксис для определения иерархии планирования. Все ресурсы, созданные с помощью CREATE RESOURCE, имеют одну и ту же структуру иерархии, но могут отличаться в некоторых аспектах. Каждая загрузка, созданная с помощью CREATE WORKLOAD, поддерживает несколько автоматически созданных узлов планирования для каждого ресурса. Дочерняя загрузка может быть создана внутри другой родительской загрузки. Вот пример, который определяет точно такую же иерархию, как и XML-конфигурация выше:

Имя листовой загрузки без дочерних узлов может быть использовано в настройках запроса SETTINGS workload = 'name'.

Чтобы настроить загрузку можно использовать следующие настройки:

  • priority - родственные нагрузки обслуживаются в соответствии со статическими значениями приоритета (меньшее значение означает более высокий приоритет).
  • weight - родственные нагрузки с одинаковым статическим приоритетом делят ресурсы в соответствии с весами.
  • max_io_requests - лимит на количество одновременных запросов ввода-вывода в этой нагрузке.
  • max_bytes_inflight - лимит на общее количество байт в процессе выполнения для одновременных запросов в этой нагрузке.
  • max_bytes_per_second - лимит на скорость чтения или записи байт для этой нагрузки.
  • max_burst_bytes - максимальное количество байт, которые могут обрабатываться нагрузкой без ограничения (для каждого ресурса независимо).
  • max_concurrent_threads - лимит на количество потоков для запросов в этой нагрузке.

Все ограничения, заданные через настройки нагрузки, независимы для каждого ресурса. Например, нагрузка с max_bytes_per_second = 10485760 будет иметь лимит пропускной способности 10 МБ/с для каждого ресурса чтения и записи независимо. Если требуется общее ограничение для чтения и записи, рассмотрите возможность использования одного и того же ресурса для доступа READ и WRITE.

Нет возможности указать разные иерархии нагрузок для разных ресурсов. Но есть способ указать разные значения настройки нагрузки для конкретного ресурса:

Также обратите внимание, что нагрузка или ресурс не могут быть удалены, если на них ссылается другая нагрузка. Чтобы обновить определение нагрузки используйте запрос CREATE OR REPLACE WORKLOAD.

примечание

Настройки нагрузки переводятся в соответствующий набор узлов планирования. Для более детальной информации смотрите описание узлов планирования типов и опций.

Планирование CPU

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

Когда сервер ClickHouse выполняет множество одновременных запросов с несколькими потоками и все слоты CPU заняты, достигается перегруженное состояние. В перегруженном состоянии каждый освобожденный слот CPU переназначается в соответствующую нагрузку в соответствии с политиками планирования. Для запросов, разделяющих одну и ту же нагрузку, слоты выделяются по круговой системе. Для запросов в разных нагрузках слоты выделяются в соответствии с весами, приоритетами и лимитами, указанными для нагрузок.

Время CPU используется потоками, когда они не заблокированы и работают над ресурсозатратными задачами. Для целей планирования различают два типа потоков:

  • Мастером поток — первый поток, который начинает работать над запросом или фоновой деятельностью, такой как слияние или мутация.
  • Рабочий поток — дополнительные потоки, которые мастер может запускать для выполнения ресурсовоздействующих задач.

Может быть целесообразно использовать отдельные ресурсы для мастер- и рабочих потоков, чтобы добиться лучшей отзывчивости. Высокое количество рабочих потоков может легко монополизировать ресурс CPU, когда используются высокие значения установки max_threads для запросов. В этом случае входящие запросы должны блокироваться и ждать слот CPU для начала выполнения их мастер-потока. Чтобы избежать этого, можно использовать следующую конфигурацию:

Это создаст отдельные лимиты для мастер- и рабочих потоков. Даже если все 100 слотов рабочей CPU заняты, новые запросы не будут заблокированы, пока не появятся доступные слоты мастер CPU. Они начнут выполняться с одного потока. Позже, если слоты рабочего CPU станут доступны, такие запросы смогут увеличить количество своих рабочих потоков. С другой стороны, такой подход не связывает общее количество слотов с количеством процессоров CPU, и запуск слишком большого количества одновременных потоков повлияет на производительность.

Ограничение конкуренции мастер-потоков не ограничит количество одновременных запросов. Слоты CPU могут быть освобождены в середине выполнения запроса и вновь захвачены другими потоками. Например, 4 одновременных запроса с лимитом 2 одновременных мастер-потока могут все выполняться параллельно. В этом случае каждый запрос получит 50% ресурса CPU. Отдельная логика должна быть использована для ограничения количества одновременных запросов, и в настоящее время это не поддерживается для нагрузок.

Можно использовать отдельные лимиты конкуренции потоков для нагрузок:

Этот пример конфигурации обеспечивает независимые пуллы слотов CPU для администраторов и производства. Пуллы для производства делятся между аналитикой и загрузкой данных. Более того, если пул производства оказывается перегруженным, 9 из 10 освобожденных слотов будут переназначены для аналитических запросов, если это необходимо. Запросы на загрузку данных получат только 1 из 10 слотов в периоды перегрузки. Это может улучшить задержку запросов, обращенных к пользователям. Аналитика имеет свой собственный лимит в 60 одновременных потоках, всегда оставляя по крайней мере 40 потоков для поддержки загрузки данных. Когда нет перегрузок, загрузка может использовать все 100 потоков.

Чтобы исключить запрос из планирования CPU, установите настройку запроса use_concurrency_control в 0.

Планирование CPU еще не поддерживается для слияний и мутаций.

осторожно

Планирование слотов предоставляет способ контролировать конкуренцию запросов, но пока не гарантирует справедливое распределение времени CPU. Для этого требуется дальнейшая разработка предварительного выбора слотов CPU и эта функция будет поддерживаться позже.

примечание

Объявление ресурса CPU отключает действие concurrent_threads_soft_limit_num и concurrent_threads_soft_limit_ratio_to_cores настроек. Вместо этого настройка нагрузки max_concurrent_threads используется для ограничения числа CPU, выделенных для конкретной нагрузки. Чтобы добиться прежнего поведения создайте только ресурс WORKER THREAD, установите max_concurrent_threads для нагрузки all на то же значение, что и concurrent_threads_soft_limit_num, и используйте настройку запроса workload = "all". Эта конфигурация соответствует настройке concurrent_threads_scheduler с установленным значением "fair_round_robin".

Хранение нагрузок и ресурсов

Определения всех нагрузок и ресурсов в форме запросов CREATE WORKLOAD и CREATE RESOURCE хранятся постоянно либо на диске по пути workload_path, либо в ZooKeeper по пути workload_zookeeper_path. Рекомендуется использовать хранилище ZooKeeper для достижения согласованности между узлами. В качестве альтернативы можно использовать условие ON CLUSTER вместе с дисковым хранилищем.

Строгий доступ к ресурсам

Чтобы заставить все запросы следовать политикам планирования ресурсов, существует настройка сервера throw_on_unknown_workload. Если она установлена в true, то каждый запрос должен использовать действительную настройку запроса workload, в противном случае возникает исключение RESOURCE_ACCESS_DENIED. Если она установлена в false, то такой запрос не использует планировщик ресурсов, т.е. он получит неограниченный доступ к любому RESOURCE.

примечание

Не устанавливайте throw_on_unknown_workload в true, если не выполнена команда CREATE WORKLOAD default. Это может привести к проблемам при запуске сервера, если запрос без явной настройки workload исполняется во время запуска.

См. также