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

Автоматическое прерывание запросов: руководство по настройке SQL Block Rule и Workload Policy

Автоматическое прерывание запросов представляет собой защитный механизм, который не позволяет длительным или ресурсоёмким запросам нарушать стабильность кластера. Когда запрос превышает заданные ограничения по ресурсам или времени, механизм автоматически завершает его, не позволяя одному запросу исчерпать системные ресурсы или замедлить выполнение других рабочих нагрузок.

Doris предоставляет две стратегии прерывания, которые действуют на разных этапах жизненного цикла запроса:

КритерийSQL Block Rule (прерывание на этапе планирования)Workload Policy (прерывание во время выполнения)
Этап примененияЭтап планирования запроса: перехват до начала выполненияЭтап выполнения запроса: прерывание во время выполнения
Основание для решенияОценочный объём сканирования, сопоставление SQL с шаблонамиПоказатели в реальном времени: фактическое время выполнения, использование памяти, объём сканирования
ТочностьОсновано на оценке стоимости, возможны ложные срабатыванияОсновано на мониторинге в реальном времени, более высокая точность
Сценарий использованияБлокировка известных опасных шаблонов SQL и полного сканирования таблицПрерывание выполняющихся запросов, превышающих ожидаемые показатели
Поддерживаемые операторыЛюбые операторы (DDL, DML)select, insert into select, stream load, routine load

SQL Block Rule​

SQL Block Rule перехватывает операторы, соответствующие определённым шаблонам, на этапе планирования, не допуская их выполнения. Обычно эти правила настраивают администраторы баз данных, чтобы предотвратить известные рискованные операции, такие как полное сканирование таблиц и вызовы опасных функций.

Свойства правила​

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

СвойствоОписаниеЗначение
sqlРегулярное выражение для сопоставления с текстом запросаСтрока с регулярным выражением Java
sqlHashХеш для сопоставления с текстом запросаСтрока с хешем SQL
cardinalityМаксимальное количество строк, разрешённых для сканированияПоложительное целое число
partition_numМаксимальное количество партиций, разрешённых для сканированияПоложительное целое число
tablet_numМаксимальное количество tablet, разрешённых для сканированияПоложительное целое число
require_partition_filterТребуется ли указывать эффективный фильтр по партициям в запросах к партиционированным внутренним таблицам и таблицам Hive. Поддерживается в Doris 4.0.7 и более поздних версиях ветки 4.0, а также в Doris 4.1.4 и более поздних версиях ветки 4.1."true" или "false"
globalЯвляется ли правило глобальным"true" (глобальное) / "false" (действует только для привязанных пользователей)
enableВключено ли правило"true" / "false"

Сценарии использования и примеры​

Сценарий 1: ограничение количества сканируемых строк​

Проблема: операции полного сканирования таблицы, такие как SELECT * FROM t, потребляют значительные ресурсы IO и CPU на BE.

Решение: задайте верхний предел количества строк, сканируемых в одной таблице. Запросы, превышающие этот порог, блокируются.

CREATE SQL_BLOCK_RULE rule_card 
PROPERTIES
(
"cardinality" = "1000",
"global" = "true",
"enable" = "true"
);

Если при сканировании одной таблицы требуется прочитать более 1000 строк, запрос отклоняется.

Сценарий 2: ограничение количества сканируемых партиций​

Проблема: сканирование слишком большого количества партиций значительно увеличивает потребление ресурсов CPU на BE. При запросах к внешним таблицам оно также создаёт дополнительную нагрузку на сеть и увеличивает затраты на получение метаданных. Обычно это происходит из-за отсутствующих или некорректно заданных условий фильтрации по партициям.

Решение: задайте максимальное количество партиций, которые может сканировать один запрос.

CREATE SQL_BLOCK_RULE rule_part_num 
PROPERTIES
(
"partition_num" = "30",
"global" = "true",
"enable" = "true"
);

Если при сканировании одной таблицы требуется просмотреть более 30 партиций, запрос отклоняется.

Сценарий 3: ограничение количества сканируемых tablet​

Проблема: сканирование слишком большого количества tablet также значительно увеличивает потребление ресурсов CPU на BE.

Решение: задайте максимальное количество tablet, которые может сканировать один запрос.

CREATE SQL_BLOCK_RULE rule_tablet_num 
PROPERTIES
(
"tablet_num" = "200",
"global" = "true",
"enable" = "true"
);

Если при сканировании одной таблицы требуется просмотреть более 200 tablet, запрос отклоняется.

Сценарий 4: блокировка определённых шаблонов SQL​

Проблема: некоторые шаблоны запросов, например вызовы определённых функций или выполнение опасных операций, могут приводить к чрезмерной вычислительной сложности или длительному планированию.

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

Пример 1: блокировка использования определённой функции

Следующее правило блокирует все запросы, содержащие функцию abs:

CREATE SQL_BLOCK_RULE rule_abs
PROPERTIES(
"sql"="(?i)abs\\s*\\(.+\\)",
"global"="true",
"enable"="true"
);

Пояснение регулярного выражения:

  • (?i): сопоставление без учёта регистра
  • abs: имя целевой функции
  • \\s*: допускает любые пробельные символы между именем функции и открывающей скобкой
  • \\(.+\\): сопоставляется с аргументами функции

Пример 2: блокировка опасных операций

Аналогичным образом с помощью регулярных выражений можно блокировать set global (для предотвращения непреднамеренного изменения переменных) или truncate table (для предотвращения непреднамеренного удаления данных).

Регулярные выражения используют синтаксис Java. Подробности см. в документации Pattern.

Сценарий 5: настройка правил для определённых пользователей​

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

Решение: создайте неглобальное правило и привяжите его к нужному пользователю.

  1. При создании правила задайте для global значение "false":

    CREATE SQL_BLOCK_RULE rule_001
    PROPERTIES (
    "sql"="select \\* from t",
    "global" = "false",
    "enable" = "true"
    );
  2. Привяжите правило к нужному пользователю:

    SET PROPERTY FOR 'root' 'SQL_block_rules' = 'rule_001';
  3. Убедитесь, что правило действует:

    MySQL root@127.0.0.1:test> select * from t;
    (1105, 'errCode = 2, detailMessage = errCode = 2, detailMessage = SQL match regex SQL block rule: rule_001')

Управление несколькими правилами:

  • Чтобы привязать к пользователю несколько правил, перечислите их имена через запятую в списке правил, например 'rule_001,rule_002'.
  • Чтобы удалить все правила пользователя, задайте пустую строку в качестве списка правил, например SET PROPERTY FOR 'root' 'SQL_block_rules' = '';.

Сценарий 6: обязательная фильтрация по партициям​

В партиционированных внутренних таблицах и таблицах Hive отсутствие фильтров по столбцам партиционирования может привести к сканированию всех партиций. Для запросов к таким партиционированным таблицам можно сделать эффективный фильтр по партициям обязательным:

CREATE SQL_BLOCK_RULE rule_require_partition_filter
PROPERTIES
(
"require_partition_filter" = "true",
"global" = "true",
"enable" = "true"
);

После включения правила запросы вида SELECT * FROM partitioned_table будут блокироваться. Запросы с предикатами по партициям, например SELECT * FROM partitioned_table WHERE dt = '2024-01-01', по-прежнему смогут выполняться.

Примечания​

  • Метод оценки: количество сканируемых строк, партиций и tablet оценивается на этапе планирования для наихудшего случая (учитывается только отсечение партиций и tablet; другие условия фильтрации не учитываются). Запросы, у которых фактический объём сканирования меньше заданного значения, также могут блокироваться.
  • Влияние на производительность: сопоставление с регулярными выражениями требует значительных вычислительных ресурсов. Слишком большое количество правил с регулярными выражениями или их чрезмерная сложность создают существенную нагрузку на CPU на FE, поэтому добавляйте их с осторожностью.
  • Временное отключение правила: измените значение свойства "enable" правила на "false", чтобы временно отключить его без удаления.

Workload Policy​

Workload Policy отслеживает показатели в реальном времени на этапе выполнения запроса и применяет такие действия, как прерывание или изменение переменных, к запросам, превышающим ожидаемые показатели. В отличие от статической оценки SQL Block Rule на этапе планирования, Workload Policy принимает решения на основе фактических данных во время выполнения и обеспечивает более высокую точность.

Workload Policy поддерживается начиная с Doris 2.1. Поддержка различных типов рабочих нагрузок приведена ниже:

Тип рабочей нагрузкиПоддерживается
selectДа
insert into selectДа
insert into valuesНет
stream loadДа
routine loadДа
backupНет
compactionНет

Создание и настройка​

Для создания политики используйте команду CREATE WORKLOAD POLICY. Требуется привилегия admin_priv.

Базовый синтаксис:

CREATE WORKLOAD POLICY <policy_name>
CONDITIONS(<condition_expr>)
ACTIONS(<action_expr>)
PROPERTIES('<key>'='<value>');

В следующем примере создаётся политика, которая отменяет запросы, выполняющиеся дольше 1000 мс:

CREATE WORKLOAD POLICY test_cancel_policy
CONDITIONS(query_time > 1000)
ACTIONS(cancel_query)
PROPERTIES('enabled'='true');

Условия срабатывания​

Условия определяют, когда срабатывает политика. Несколько условий разделяются запятыми (,), что соответствует логическому AND.

УсловиеОписание
usernameИмя пользователя, передаваемое в запросе. Начиная с Doris 4.1.3 его можно сочетать с показателями выполнения на стороне BE, такими как query_time, be_scan_rows, be_scan_bytes и query_be_memory_bytes, для запуска cancel_query. Это условие поддерживает только оператор равенства (=), а имя пользователя не может быть пустым
be_scan_rowsКоличество строк, сканируемых SQL-запросом в рамках одного процесса BE. При параллельном выполнении значения суммируются
be_scan_bytesКоличество байтов, сканируемых SQL-запросом в рамках одного процесса BE. При параллельном выполнении значения суммируются (единица измерения: байты)
query_timeВремя выполнения SQL-запроса в одном процессе BE (единица измерения: миллисекунды)
query_be_memory_bytesОбъём памяти, используемой SQL-запросом в рамках одного процесса BE. При параллельном выполнении значения суммируются (единица измерения: байты)

Действия​

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

ДействиеОписание
cancel_queryОтменить запрос

Свойства политики​

СвойствоОписаниеЗначение по умолчанию
enabledВключена ли политика. Значение: true или falsetrue
priorityПриоритет в диапазоне от 0 до 100. Чем больше значение, тем выше приоритет. Если запрос соответствует нескольким политикам, действует только политика с наивысшим приоритетом0
workload_groupИмя привязанной группы Workload Group. Если оно задано, политика применяется только к запросам в этой Workload Group. Если значение пустое, политика применяется ко всем запросамПустое значение (глобально)

Привязка к Workload Group​

Чтобы ограничить действие политики определённой группой Workload Group, привяжите её с помощью свойства workload_group:

CREATE WORKLOAD POLICY test_cancel_big_query
CONDITIONS(query_time > 1000)
ACTIONS(cancel_query)
PROPERTIES('workload_group'='normal');
В облачном режиме требуется полное имя

Приведённая выше форма 'workload_group'='normal' допустима только в необлачном режиме (с совместным размещением хранилища и вычислений). В облачном режиме (с разделением хранилища и вычислений) требуется полная форма <compute_group>.<workload_group>, например 'workload_group'='compute_group_a.normal'; иначе оператор завершается с ошибкой: workload_group must be '<compute_group>.<workload_group>' in cloud mode. Подробности см. в разделе CREATE WORKLOAD POLICY.

Примеры​

Пример 1: прерывание длительных запросов​

Из следующего журнала аудита видно, что SQL-запрос обычно выполняется за 4.5 секунды:

MySQL [hits]> SELECT REGEXP_REPLACE(Referer, '^https?://(?:www\\.)?([^/]+)/.*$', '\\1') AS k, AVG(length(Referer)) AS l, COUNT(*) AS c, MIN(Referer) FROM hits WHERE Referer <> '' GROUP BY k HAVING COUNT(*) > 100000 ORDER BY l DESC LIMIT 25;
+-----------------------------------------------------------------------+------------------+----------+---------------------------------------------------------------------------------------------------------------------+
| k | l | c | min(Referer) |
+-----------------------------------------------------------------------+------------------+----------+---------------------------------------------------------------------------------------------------------------------+
| 1 | 85.4611926713085 | 67259319 | http://%26ad%3D1%25EA%25D0%26utm_source=web&cd=19590&input_onlist/би-2 место будущей кондицин |
| http:%2F%2Fwwww.regnancies/search&evL8gE&where=all&filmId=bEmYZc_WTDE | 69 | 207347 | http:%2F%2Fwwww.regnancies/search&evL8gE&where=all&filmId=bEmYZc_WTDE |
| http://новострашная | 31 | 740277 | http://новострашная |
| http://loveche.html?ctid | 24 | 144901 | http://loveche.html?ctid |
| http://rukodeliveresult | 23 | 226135 | http://rukodeliveresult |
| http://holodilnik.ru | 20 | 133893 | http://holodilnik.ru |
| http://smeshariki.ru | 20 | 210736 | http://smeshariki.ru |
| http:%2F%2Fviewtopic | 20 | 391115 | http:%2F%2Fviewtopic |
| http:%2F%2Fwwww.ukr | 19 | 655178 | http:%2F%2Fwwww.ukr |
| http:%2F%2FviewType | 19 | 148907 | http:%2F%2FviewType |
| http://state=2008 | 17 | 139630 | http://state=2008 |
+-----------------------------------------------------------------------+------------------+----------+---------------------------------------------------------------------------------------------------------------------+
11 rows in set (4.50 sec)

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

CREATE WORKLOAD POLICY test_cancel_3s_query
CONDITIONS(query_time > 3000)
ACTIONS(cancel_query);

Выполните тот же SQL-запрос ещё раз: теперь он будет прерван:

MySQL [hits]> SELECT REGEXP_REPLACE(Referer, '^https?://(?:www\\.)?([^/]+)/.*$', '\\1') AS k, AVG(length(Referer)) AS l, COUNT(*) AS c, MIN(Referer) FROM hits WHERE Referer <> '' GROUP BY k HAVING COUNT(*) > 100000 ORDER BY l DESC LIMIT 25;
ERROR 1105 (HY000): errCode = 2, detailMessage = (127.0.0.1)[CANCELLED]query cancelled by workload policy,id:12345

Пример 2: прерывание ресурсоёмких запросов определённого пользователя во время выполнения​

Начиная с Doris 4.1.3 можно сочетать username с показателями выполнения на стороне BE, чтобы отменять только запросы определённого пользователя. Следующая политика отменяет запросы пользователя test_user, если время их выполнения в одном BE превышает 3000 мс:

CREATE WORKLOAD POLICY cancel_user_long_query
CONDITIONS(username='test_user', query_time > 3000)
ACTIONS(cancel_query);

Аналогичным образом username можно сочетать с be_scan_rows, be_scan_bytes или query_be_memory_bytes, чтобы ограничить объём сканирования или использование памяти BE для определённого пользователя.

Примечания​

  • Задержка асинхронного выполнения: политики проверяются асинхронным потоком каждые 500 мс, поэтому их применение происходит с некоторой задержкой. Очень короткие запросы могут завершиться до проверки и обойти политику.
  • Механизм приоритетов: запрос может соответствовать нескольким политикам, но действует только политика с наивысшим приоритетом (наибольшим значением priority).
  • Ограничение на изменение: в настоящее время нельзя напрямую изменить действие и условие существующей политики. Удалите политику и создайте её заново.