У ClickHouse отличная встроенная интроспекция: он рассказывает о себе почти всё через системные таблицы. Вопрос только в том, чтобы смотреть на нужное — и до инцидента, а не во время.
Системные таблицы, которые нужно знать
| Таблица | Зачем |
|---|---|
system.query_log | История запросов: длительность, память, ошибки |
system.parts | Куски данных: количество, размер, партиции |
system.merges | Идущие прямо сейчас слияния |
system.mutations | Мутации (ALTER UPDATE/DELETE) и их прогресс |
system.replicas | Здоровье репликации, отставание, read-only |
system.replication_queue | Очередь задач репликации и застрявшие записи |
system.processes | Что выполняется прямо сейчас |
system.asynchronous_metrics | Метрики состояния (память, диски, файлы) |
system.metrics / system.events | Счётчики и события сервера |
system.errors | Накопленные ошибки по типам |
system.disks | Свободное место по дискам |
Включите query_log (обычно включён по умолчанию) — без него разбор «почему вчера в 14:00 было плохо» превращается в гадание. И сразу задайте ему TTL, иначе он вырастет до неприличных размеров:
ALTER TABLE system.query_log
MODIFY TTL event_date + INTERVAL 30 DAY;То же касается system.part_log, system.trace_log и прочих *_log.
Метрики в Prometheus
Два источника:
- Встроенный эндпоинт ClickHouse. Сервер умеет отдавать метрики в формате Prometheus напрямую (настраивается секцией
prometheusв конфигурации: порт, путь, какие наборы отдавать —metrics,events,asynchronous_metrics,status_info). - Метрики оператора. И официальный оператор, и Altinity экспортируют собственные метрики о состоянии кластера и раскатки — они дополняют картину со стороны Kubernetes.
Плюс стандартный kube-state-metrics и метрики нод: рестарты подов, OOMKilled, заполнение PVC. Инциденты в Kubernetes чаще видно именно там.
Минимальный набор алертов
Правило: алерт должен предсказывать боль и иметь понятное действие. Всё остальное — дашборд.
Критичные (будят):
- Реплика в read-only —
system.replicas.is_readonly. Почти всегда означает проблему с Keeper. Вставки не проходят. - Keeper потерял кворум — нет лидера или меньше половины узлов живо. Всё встало.
- Свободное место на диске < 15–20% — слияния скоро встанут (см. часть 4).
- Рост числа активных кусков — предвестник
Too many parts. - Отставание реплики растёт устойчиво —
absolute_delay. Чтение с реплик отдаёт устаревшие данные, а переключение станет рискованным.
Важные (в рабочее время):
- очередь репликации не разгружается (
queue_sizeдержится высоким); - всплеск ошибок в
system.errorsиExceptionWhileProcessingвquery_log; - рост p95/p99 времени запросов;
- зависшие мутации (
system.mutations,is_done = 0долгое время); - рост объёма временных файлов на диске;
- рестарты подов и приближение памяти к лимиту.
Готовые запросы для дежурного
-- 1. Здоровье репликации: главный запрос при инциденте
SELECT database, table, is_readonly, is_session_expired,
absolute_delay, queue_size, inserts_in_queue, merges_in_queue
FROM system.replicas
WHERE is_readonly OR is_session_expired OR absolute_delay > 60 OR queue_size > 100;
-- 2. Таблицы, подбирающиеся к пределу по кускам
SELECT database, table, count() AS parts,
formatReadableSize(sum(bytes_on_disk)) AS size
FROM system.parts
WHERE active
GROUP BY database, table
ORDER BY parts DESC
LIMIT 10;
-- 3. Место на дисках
SELECT name, path,
formatReadableSize(free_space) AS free,
formatReadableSize(total_space) AS total,
round(100 * (total_space - free_space) / total_space, 1) AS used_pct
FROM system.disks;
-- 4. Самые тяжёлые запросы за сутки
SELECT user, query_duration_ms,
formatReadableSize(memory_usage) AS mem,
formatReadableSize(read_bytes) AS read,
substring(query, 1, 120) AS q
FROM system.query_log
WHERE type = 'QueryFinish' AND event_time > now() - INTERVAL 1 DAY
ORDER BY query_duration_ms DESC
LIMIT 20;
-- 5. Застрявшие мутации
SELECT database, table, mutation_id, command, parts_to_do, latest_fail_reason
FROM system.mutations
WHERE is_done = 0;Дашборд, который действительно используют
Не пытайтесь показать всё. Рабочий минимум на одном экране:
- запросы: RPS, p95/p99 длительности, доля ошибок;
- вставки: строк/сек, число кусков по таблицам;
- репликация: максимальное отставание, размер очереди, число read-only реплик;
- ресурсы: память относительно лимита, CPU, свободное место на дисках;
- слияния: количество и объём активных;
- Keeper: наличие лидера, латентность, число сессий.
Готовые дашборды для Grafana есть и у сообщества, и у Altinity — разумно взять их за основу и подрезать под себя.
Профилирование конкретного запроса
Когда нужно понять, почему конкретный запрос медленный:
-- Что реально прочитано и сколько потрачено
EXPLAIN indexes = 1
SELECT ... ;
-- Подробности выполнения из лога
SELECT read_rows, read_bytes, result_rows, memory_usage,
query_duration_ms, ProfileEvents
FROM system.query_log
WHERE query_id = '...' AND type = 'QueryFinish';Главный вопрос при разборе: сколько строк прочитано против того, сколько вернулось. Если ради тысячи строк результата прочитаны миллиарды — проблема в ORDER BY или в отсутствии фильтра по ключу, а не в железе. Возвращайтесь к части 5.
Дальше: Часть 9. Бэкапы и восстановление.