Query Governance: ограничения и квоты
В production-системах ClickHouse одновременно работают десятки аналитиков, ETL-задач и дашбордов. Без контроля один неоптимальный запрос — полный скан 100-миллиардной таблицы без фильтра по партиции — способен исчерпать RAM-сервера и вытеснить остальные запросы. Query governance — это набор механизмов ClickHouse, которые задают жёсткие лимиты на ресурсы, время выполнения и объём результатов: на уровне отдельного запроса, пользователя, роли или временного интервала.
Лимиты на уровне запроса
Самый прямой механизм защиты — задать ограничения непосредственно в SETTINGS-секции запроса.
max_rows_to_read
Ограничивает число строк, которые ClickHouse может прочитать для выполнения запроса. При превышении — ошибка TOO_MANY_ROWS.
-- Запрос сканирует не более 1 млн строк
SELECT count()
FROM huge_table
SETTINGS max_rows_to_read = 1000000;
Это ограничение проверяется на уровне сканирования (до агрегации), что делает его эффективным инструментом для предотвращения случайных полных сканов.
max_result_rows
Ограничивает число строк в итоговом результате. При превышении — ошибка TOO_MANY_ROWS_OR_BYTES.
-- Результат не может содержать более 10 000 строк
SELECT user_id, event_type, count() AS cnt
FROM events
GROUP BY user_id, event_type
SETTINGS max_result_rows = 10000;
max_execution_time
Максимальное время выполнения запроса в секундах. При превышении — ошибка TIMEOUT_EXCEEDED.
-- Запрос будет остановлен через 30 секунд
SELECT sum(price)
FROM orders
WHERE region = 'EU'
SETTINGS max_execution_time = 30;
max_execution_time — мягкий лимит. Проверка таймаута происходит при чтении следующего блока данных. Один тяжёлый блок (например, сложный JOIN с десятками миллионов строк) может задержать отмену на несколько секунд после истечения таймаута. Для жёстких realtime требований дополнительно используйте max_rows_to_read.
max_result_bytes
Ограничивает размер результата в байтах. Удобно для защиты от запросов, возвращающих слишком большие текстовые блоки.
-- Результат не более 100 МБ
SELECT *
FROM logs
WHERE level = 'ERROR'
SETTINGS max_result_bytes = 104857600;
Комбинирование настроек
Несколько ограничений можно задать в одной SETTINGS-секции:
SELECT query_id, memory_usage, read_rows
FROM system.query_log
WHERE type = 'QueryFinish'
SETTINGS
max_threads = 4,
max_execution_time = 30,
max_result_rows = 1000;
Ограничения на уровне роли
Задавать лимиты в каждом запросе неудобно и ненадёжно — аналитик может их убрать. Надёжнее зафиксировать ограничения в роли пользователя.
-- Создать роль с ограничением на число строк результата
CREATE ROLE analyst_role SETTINGS max_result_rows = 10000;
-- Назначить роль пользователю
GRANT analyst_role TO analyst_user;
Теперь пользователь analyst_user не сможет получить более 10 000 строк в одном запросе, даже если попытается переопределить настройку через SETTINGS.
-- Создать роль для ETL с ограничением времени
CREATE ROLE etl_role SETTINGS max_execution_time = 300;
GRANT etl_role TO etl_service;
Квоты (QUOTA)
Квоты ограничивают потребление ресурсов за временной интервал — в отличие от max_result_rows (per-query лимит), квота накапливается и сбрасывается по расписанию.
Синтаксис CREATE QUOTA
-- Квота: не более 1 млн строк результата в сутки
CREATE QUOTA daily_quota
FOR INTERVAL 1 DAY MAX result_rows = 1000000
TO analyst_role;
-- Квота с несколькими интервалами
CREATE QUOTA reporting_quota
FOR INTERVAL 10 SECOND MAX result_rows = 10
FOR INTERVAL 1 HOUR MAX result_rows = 100000
FOR INTERVAL 1 DAY MAX result_rows = 1000000
TO reporter_role;
При исчерпании квоты следующий запрос, превышающий лимит, получает ошибку QUOTA_EXCEEDED.
Просмотр квот
-- Текущие квоты в системе
SHOW QUOTAS;
-- Использование квоты конкретным пользователем
SELECT *
FROM system.quota_usage;
Сравнение механизмов governance
| Ограничение | Scope | Ошибка при превышении | Пример |
|---|---|---|---|
max_rows_to_read | Per-query, на уровне сканирования | TOO_MANY_ROWS | SETTINGS max_rows_to_read = 1e6 |
max_result_rows | Per-query, на уровне результата | TOO_MANY_ROWS_OR_BYTES | SETTINGS max_result_rows = 10000 |
max_execution_time | Per-query, по времени | TIMEOUT_EXCEEDED | SETTINGS max_execution_time = 30 |
max_result_bytes | Per-query, по размеру результата | TOO_MANY_ROWS_OR_BYTES | SETTINGS max_result_bytes = 1e8 |
| Role SETTINGS | Per-role, для всех запросов пользователя | Зависит от настройки | CREATE ROLE r SETTINGS max_result_rows = 1000 |
| QUOTA FOR INTERVAL | Накопительный лимит за период | QUOTA_EXCEEDED | FOR INTERVAL 1 DAY MAX result_rows = 1e6 |
Ключевые выводы
max_rows_to_readзащищает от случайных full-scan запросов — ошибкаTOO_MANY_ROWSвозникает до агрегации, на уровне сканирования.max_execution_timeзадаёт таймаут в секундах, но является мягким лимитом: проверка происходит между блоками, не внутри обработки блока.- Role-level
SETTINGSнадёжнее per-query SETTINGS: пользователь не может превысить ограничение, заданное в роли. CREATE QUOTA FOR INTERVALограничивает суммарное потребление за временной период — в отличие от per-query лимитов, квота накапливается между запросами.- Комбинируйте механизмы: per-query SETTINGS для ad-hoc защиты, роли для системного управления, квоты для fair-use политики.