Мониторинг: дашборды «Кластер» и «Запросы» #123
No files matched your search
@@ -133,8 +133,11 @@ keeper, и обе ноды читают её целиком — запрос в
|
|||||||
посетителям продиктован [ADR 0007](0007-clickhouse-access.md): своя учётка
|
посетителям продиктован [ADR 0007](0007-clickhouse-access.md): своя учётка
|
||||||
Grafana заведена там ровно затем, чтобы её запросы были отличимы от
|
Grafana заведена там ровно затем, чтобы её запросы были отличимы от
|
||||||
человеческих.
|
человеческих.
|
||||||
Отдельная панель ошибок понадобилась под здешний же сюжет про тяжёлый
|
Отдельная панель ошибок понадобилась потому, что отказ достаётся шарду: в
|
||||||
`GROUP BY`: отказ по памяти случается на шарде, и текст ошибки — единственное
|
замере с принудительным пределом памяти каждая нода записала свой, и строка
|
||||||
место, где написано, на каком. Устройство обоих дашбордов —
|
шарда видна только без фильтра по инициатору. Здешний сюжет про тяжёлый
|
||||||
|
`GROUP BY` при этом кончается не отказом, а сбросом на диск
|
||||||
|
([ADR 0004](0004-resource-limits.md)) — отказ на стенде приходится вызывать
|
||||||
|
настройкой. Устройство обоих дашбордов —
|
||||||
[cluster.md](../architecture/monitoring/cluster.md) и
|
[cluster.md](../architecture/monitoring/cluster.md) и
|
||||||
[queries.md](../architecture/monitoring/queries.md).
|
[queries.md](../architecture/monitoring/queries.md).
|
||||||
@@ -17,14 +17,17 @@
|
|||||||
| Keeper | встроенные метрики keeper через Prometheus |
|
| Keeper | встроенные метрики keeper через Prometheus |
|
||||||
| Распределённый DDL | `system.distributed_ddl_queue` |
|
| Распределённый DDL | `system.distributed_ddl_queue` |
|
||||||
|
|
||||||
Окно у дашборда — полчаса, обновление — полминуты, и это выбрано под нагрузку.
|
Окно у дашборда — полчаса, обновление — полминуты. Пачка из
|
||||||
Вставка на стенде занимает секунды, а слияния разбирают её следы за минуту-две;
|
`make generate-batch` проходит за двадцать секунд, и на получасовой оси это
|
||||||
в часовом окне и подъём, и оседание схлопнулись бы в один столбик. Урок здесь —
|
узкий всплеск в пару столбиков. Шире окно не берут, чтобы всплеск не выродился
|
||||||
форма кривой, и окно подобрано так, чтобы форма была видна.
|
в точку; уже — чтобы не промахнуться мимо него, заглянув через минуту, и чтобы
|
||||||
|
рядом осталось спокойное время: подъём читается только над полом. Кто хочет
|
||||||
|
разглядеть форму вплотную, растягивает окно мышью по самому всплеску.
|
||||||
|
|
||||||
Две панели окна не знают, и обе по делу. «Части» показывают состояние сейчас:
|
Панель частей окна не знает вовсе. `system.parts` — опись живых частей, и
|
||||||
`system.parts` — опись живых частей, времени в ней нет вовсе. Очередь DDL
|
вопрос к ней про сейчас: колонки со временем в ней есть, но ни одна не годится
|
||||||
показывает последние двадцать записей: в спокойные полчаса она пуста, а пустая
|
в ось. Очередь DDL окна тоже не знает, но по другой причине: она показывает
|
||||||
|
последние двадцать записей, потому что в спокойные полчаса пуста, а пустая
|
||||||
панель ничего бы не рассказала.
|
панель ничего бы не рассказала.
|
||||||
|
|
||||||
## Обе ноды на одной панели
|
## Обе ноды на одной панели
|
||||||
@@ -35,11 +38,15 @@
|
|||||||
Поэтому запросы идут через `clusterAllReplicas`, а `hostName()` подписывает,
|
Поэтому запросы идут через `clusterAllReplicas`, а `hostName()` подписывает,
|
||||||
чья это строка.
|
чья это строка.
|
||||||
|
|
||||||
Исключение одно — очередь распределённых DDL. Она лежит не у ноды, а в keeper,
|
Исключений два, и они разной природы. Очередь распределённых DDL лежит не у
|
||||||
и обе ноды читают из него одну и ту же очередь целиком, вместе со строками про
|
ноды, а в keeper, и обе ноды читают из него одну и ту же очередь целиком,
|
||||||
соседа. Обёртка здесь не добавила бы правды, а удвоила бы каждую строку.
|
вместе со строками про соседа. Обёртка здесь не добавила бы правды, а удвоила
|
||||||
Правило поэтому звучит так: `clusterAllReplicas` нужен там, где у каждой ноды
|
бы каждую строку — это единственная панель дашборда без неё. Правило поэтому
|
||||||
своя правда.
|
звучит так: `clusterAllReplicas` нужен там, где у каждой ноды своя правда.
|
||||||
|
|
||||||
|
Второе исключение мягче. Панель слияний читает обе ноды той же обёрткой, но
|
||||||
|
складывает их в один ряд: шарды сливают симметрично, и разделение удвоило бы
|
||||||
|
легенду ради двух одинаковых кривых.
|
||||||
|
|
||||||
## Пол на графиках — журналы самого сервера
|
## Пол на графиках — журналы самого сервера
|
||||||
|
|
||||||
@@ -53,16 +60,22 @@ ClickHouse ведёт около десятка собственных журн
|
|||||||
двух панелях читается как подъём над полом, а не как число само по себе.
|
двух панелях читается как подъём над полом, а не как число само по себе.
|
||||||
|
|
||||||
У частей и слияний пол отделим: в `system.parts` и `system.part_log` у каждой
|
У частей и слияний пол отделим: в `system.parts` и `system.part_log` у каждой
|
||||||
строки написано, какой таблице она принадлежит. Обе панели поэтому спрашивают
|
строки написано, какой таблице она принадлежит. Обе панели поэтому показывают
|
||||||
только про слои стенда. Довод в размере: за спокойные полчаса слияния съели
|
всё, кроме журналов сервера, — и у каждой на это своя причина.
|
||||||
|
|
||||||
|
Столбик у слияний шириной в минуту, а не в шаг панели. Слияния — события
|
||||||
|
редкие: на своём шаге столбик выродился бы в волосяную линию, и четыре события
|
||||||
|
за полчаса пришлось бы искать глазами.
|
||||||
|
|
||||||
|
Слияниям фильтр иначе не пережить по размеру: за спокойные полчаса они съели
|
||||||
6 492 части в системных журналах против 219 в `ods` и `stg` вместе. Оставь мы
|
6 492 части в системных журналах против 219 в `ods` и `stg` вместе. Оставь мы
|
||||||
журналы в кадре, ряды слоёв прижались бы к нулю, и панель рассказывала бы про
|
журналы в кадре, ряды слоёв прижались бы к нулю, и панель рассказывала бы про
|
||||||
уборку за сервером вместо ваших вставок. Сколько эта уборка стоит, видно этажом
|
уборку за сервером вместо приехавших данных. Сколько эта уборка стоит, видно
|
||||||
выше, на процессоре и дисках.
|
этажом выше, на процессоре и дисках.
|
||||||
|
|
||||||
Слияния показаны по слоям, а не по нодам и не по таблицам. Шарды сливают
|
Частям фильтр нужен для другого: без него в таблице десятки строк, а значит
|
||||||
симметрично, и второй ряд на слой удвоил бы легенду, ничего не добавив. Какая
|
нужен предел — и предел рвёт пару нод пополам, оставляя таблицу с одной нодой в
|
||||||
именно таблица держит части, отвечает соседняя панель.
|
кадре. С фильтром помещаются все, и предел не нужен.
|
||||||
|
|
||||||
## Что именно меряют процессор, память и диски
|
## Что именно меряют процессор, память и диски
|
||||||
|
|
||||||
@@ -109,23 +122,27 @@ Keeper — отдельный сервис, системных таблиц у
|
|||||||
|
|
||||||
- Дашборд поднимается из каталога сам, все семь панелей отвечают через
|
- Дашборд поднимается из каталога сам, все семь панелей отвечают через
|
||||||
`/api/ds/query` в окне на полчаса. Пустых нет.
|
`/api/ds/query` в окне на полчаса. Пустых нет.
|
||||||
- Обе ноды видны на всех панелях по системным таблицам, кроме одной. Без
|
- Обе ноды видны на четырёх панелях из пяти, что читают системные таблицы нод.
|
||||||
`clusterAllReplicas` осталась очередь DDL, и обёртка ей действительно не
|
Пятая — слияния, там ноды сложены намеренно. Без `clusterAllReplicas`
|
||||||
нужна: запрос с ней вернул 124 записи с `clickhouse-01` и те же 124 с
|
осталась только очередь DDL, и обёртка ей действительно не нужна: запрос с
|
||||||
`clickhouse-02`.
|
ней вернул с `clickhouse-01` и с `clickhouse-02` одно и то же число записей —
|
||||||
- Под нагрузкой. `make generate-batch` (50 626 событий одной пачкой): процессор
|
на день замера 124 и 124.
|
||||||
поднялся с 0,04–0,18 ядра до 0,54 и 0,64, запись — с 400 КиБ/с до 2,1 и
|
- Под нагрузкой. `make generate-batch` (50 626 событий одной пачкой), замер в
|
||||||
4,5 МиБ/с. Следующие десять секунд обе величины уже вернулись к прежним: на
|
десятисекундных вёдрах: процессор поднялся с 0,04–0,18 ядра до 0,54 на одной
|
||||||
таком объёме подъём и оседание укладываются в полминуты.
|
ноде и 0,64 на другой, запись — с 400 КиБ/с до 2,1 и 4,5 МиБ/с. Ведро тут
|
||||||
- Слияния под той же нагрузкой отозвались столбиком: два слияния в `stg`,
|
часть ответа: в тридцатисекундном тот же прогон даёт 0,29 и 0,21 ядра.
|
||||||
|
Следующие десять секунд обе величины уже вернулись к прежним: на таком объёме
|
||||||
|
подъём и оседание укладываются в полминуты.
|
||||||
|
- Слияния под той же нагрузкой отозвались столбиком в ряду `stg` высотой в
|
||||||
15 съеденных частей.
|
15 съеденных частей.
|
||||||
- Блочное чтение действительно молчит: за минуту вокруг вставки `OSReadBytes`
|
- Блочное чтение действительно молчит: за минуту вокруг вставки `OSReadBytes`
|
||||||
был нулевым в десяти десятисекундных окнах из четырнадцати, а обращения к
|
был нулевым в десяти десятисекундных окнах из четырнадцати, а обращения к
|
||||||
файлам шли непрерывно во всех.
|
файлам шли непрерывно во всех.
|
||||||
- Память: около 1 ГиБ занятого против 4 ГиБ отведённого на каждой ноде.
|
- Память: около 1 ГиБ занятого против 4 ГиБ отведённого на каждой ноде.
|
||||||
- Панель частей показала `ods.order_rep` по 142 строки в части против 13 046 у
|
- Панель частей показала `ods.order_rep` по 142 строки в части против 13 046 у
|
||||||
`ods.event_rep` — заказы приезжают дневным слепком, события потоком. Все
|
`ods.event_rep` — заказы приезжают дневным слепком, события потоком. Строк
|
||||||
таблицы слоёв поместились целиком, каждая обеими нодами: восемь строк.
|
вышло восемь: четыре таблицы, каждая обеими нодами. Таблиц ошибок среди них
|
||||||
|
нет, и это правда состояния — частей у них пока не появилось.
|
||||||
- Очередь DDL показала `CREATE MATERIALIZED VIEW ... ON CLUSTER` в состоянии
|
- Очередь DDL показала `CREATE MATERIALIZED VIEW ... ON CLUSTER` в состоянии
|
||||||
`Finished` и со временем по каждой ноде: `make generate-batch` поднимает
|
`Finished` и со временем по каждой ноде: `make generate-batch` поднимает
|
||||||
`clickhouse-init`, и тот заново прогоняет DDL.
|
`clickhouse-init`, и тот заново прогоняет DDL.
|
||||||
|
|||||||
@@ -27,10 +27,6 @@ Grafana ходит в ClickHouse обычным пользователем, и
|
|||||||
остальное. Спрячь мы свои запросы фильтром — цена ушла бы с глаз, а панель «Кто
|
остальное. Спрячь мы свои запросы фильтром — цена ушла бы с глаз, а панель «Кто
|
||||||
спрашивает» недосчиталась бы самого частого посетителя.
|
спрашивает» недосчиталась бы самого частого посетителя.
|
||||||
|
|
||||||
Там же, в «Кто спрашивает», стоит `default` с сотнями запросов и одной
|
|
||||||
прочитанной строкой на каждый: это `SELECT 1` от проверки состояния
|
|
||||||
контейнера, она ходит к каждой ноде раз в несколько секунд.
|
|
||||||
|
|
||||||
Различать посетителей дашборд умеет потому, что у Grafana своя учётка со своей
|
Различать посетителей дашборд умеет потому, что у Grafana своя учётка со своей
|
||||||
ролью ([ADR 0007](../../adr/0007-clickhouse-access.md)). Ходи она под
|
ролью ([ADR 0007](../../adr/0007-clickhouse-access.md)). Ходи она под
|
||||||
аналитиком, панель «Кто спрашивает» показывала бы одну строку на всех.
|
аналитиком, панель «Кто спрашивает» показывала бы одну строку на всех.
|
||||||
@@ -46,9 +42,11 @@ Grafana ходит в ClickHouse обычным пользователем, и
|
|||||||
считают только запросы инициатора: иначе один запрос человека выглядел бы
|
считают только запросы инициатора: иначе один запрос человека выглядел бы
|
||||||
тремя, а прочитанные строки сложились бы дважды.
|
тремя, а прочитанные строки сложились бы дважды.
|
||||||
|
|
||||||
Панель ошибок, наоборот, куски шардов показывает. Отказ по памяти случается
|
Панель ошибок, наоборот, куски шардов показывает: отказ случается там, где
|
||||||
там, где считали, — на шарде; инициатор в своей строке передаст только текст
|
считали, и строка шарда — единственное место, где эта нода названа. В замере с
|
||||||
ошибки, а нода, на которой всё случилось, останется неназванной.
|
принудительным пределом памяти `clickhouse-01` записал свой отказ, а
|
||||||
|
`clickhouse-02` — свой, и вторая строка видна только без фильтра по
|
||||||
|
инициатору.
|
||||||
|
|
||||||
## Пустой пользователь
|
## Пустой пользователь
|
||||||
|
|
||||||
@@ -69,6 +67,10 @@ Grafana ходит в ClickHouse обычным пользователем, и
|
|||||||
- Ошибки видны и различаются по природе: отказ по правам (`bi` без `SELECT` на
|
- Ошибки видны и различаются по природе: отказ по правам (`bi` без `SELECT` на
|
||||||
`ods`) и ошибка в тексте запроса (несуществующая колонка) легли в панель
|
`ods`) и ошибка в тексте запроса (несуществующая колонка) легли в панель
|
||||||
разными строками, каждая с полным текстом.
|
разными строками, каждая с полным текстом.
|
||||||
|
- Строка шарда в панели ошибок настоящая. Принудительный предел памяти на
|
||||||
|
распределённом `GROUP BY` дал две записи: `clickhouse-01` с
|
||||||
|
`is_initial_query = 1` и `clickhouse-02` с нулём. Вторая пропала бы, стой на
|
||||||
|
панели тот же фильтр, что на соседних.
|
||||||
- Панель «Кто спрашивает» разделила шесть учёток — `grafana`, `analyst`, `etl`,
|
- Панель «Кто спрашивает» разделила шесть учёток — `grafana`, `analyst`, `etl`,
|
||||||
`dict`, `bi`, `default` — и фоновую работу сервера. Больше всех прочитала
|
`dict`, `bi`, `default` — и фоновую работу сервера. Больше всех прочитала
|
||||||
`grafana`: 11,5 млн строк за час, своими же панелями.
|
`grafana`: 11,5 млн строк за час, своими же панелями.
|
||||||
@@ -176,7 +176,7 @@
|
|||||||
"editorType": "sql",
|
"editorType": "sql",
|
||||||
"queryType": "table",
|
"queryType": "table",
|
||||||
"format": 1,
|
"format": 1,
|
||||||
"rawSql": "SELECT\n concat(database, '.', table) AS \"Таблица\",\n hostName() AS \"Нода\",\n count() AS \"Частей\",\n sum(rows) AS \"Строк\",\n round(sum(rows) / count()) AS \"Строк в части\",\n sum(bytes_on_disk) AS \"На диске\"\nFROM clusterAllReplicas('clickstream_cluster', system.parts)\nWHERE active\n AND database != 'system'\nGROUP BY \"Таблица\", \"Нода\"\nORDER BY \"Частей\" DESC, \"Таблица\", \"Нода\"\n"
|
"rawSql": "SELECT\n concat(database, '.', table) AS \"Таблица\",\n hostName() AS \"Нода\",\n count() AS \"Частей\",\n sum(rows) AS \"Строк\",\n round(sum(rows) / count()) AS \"Строк в части\",\n sum(bytes_on_disk) AS \"На диске\"\nFROM clusterAllReplicas('clickstream_cluster', system.parts)\nWHERE active\n AND database != 'system'\nGROUP BY \"Таблица\", \"Нода\"\nORDER BY \"Таблица\", \"Нода\"\n"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
@@ -199,7 +199,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "timeseries",
|
"type": "timeseries",
|
||||||
"title": "Слияния: сколько частей они съедают в каждом слое",
|
"title": "Слияния: сколько частей они съедают",
|
||||||
"datasource": {
|
"datasource": {
|
||||||
"type": "grafana-clickhouse-datasource",
|
"type": "grafana-clickhouse-datasource",
|
||||||
"uid": "clickhouse"
|
"uid": "clickhouse"
|
||||||
@@ -241,7 +241,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"overrides": []
|
"overrides": []
|
||||||
}
|
},
|
||||||
|
"interval": "1m"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "timeseries",
|
"type": "timeseries",
|
||||||
|
|||||||
Reference in new issue
Block a user