Использование flameGraph
Для глубокого анализа производительности и отладки запросов ADQM/ClickHouse предоставляет системную таблицу system.trace_log, в которую сохраняются трассировки стека, собираемые профилировщиком запросов. Например, с помощью данных этой таблицы можно понять почему запрос выполняется слишком медленно или не так, как ожидалось — изучить последовательность операций в трассировке и найти конкретные функции запроса, которые вызывают проблемы (требуют больше всего времени или ресурсов на выполнение). Эта информация может быть полезна для оптимизации запроса и повышения его производительности.
Для визуального представления трассировок стека в виде flame-графика можно использовать агрегатную функцию flameGraph. Эта функция возвращает массив строк по данным таблицы system.trace_log, который затем можно передать в утилиту flamegraph.pl (или аналогичный инструмент) для построения flame-графика.
Синтаксис функции flameGraph
Синтаксис функции flameGraph в общем виде:
flameGraph(<trace>, [<size>], [<ptr>])
где:
-
<trace>— трассировка стека; -
<size>— количество выделенной памяти при профилировании по памяти (тип трассировкиMemory,MemorySampleилиMemoryPeak), по умолчанию —1; -
<ptr>— адрес выделенной памяти, по умолчанию —0.
Построение flame-графика
Чтобы построить профиль запроса в виде flame-графика на основе информации из таблицы system.trace_log, следуйте приведенным ниже инструкциям.
Предварительно на хостах ADQM необходимо настроить профилировщик запросов ADQM и подготовить инструменты для отрисовки flame-графиков:
-
Включите профилировщик запросов и настройте его с помощью параметров
query_profiler_*иmemory_profiler_*(см. Session Settings в документации ClickHouse). Эти параметры можно устанавливать на уровне пользователя ADQM, сессии clickhouse-client или конкретного запроса, который будет профилироваться (подробнее в статье Управление настройками пользователя). Например, рекомендуется назначить параметрамquery_profiler_real_time_period_nsи/илиquery_profiler_cpu_time_period_nsзначение10000000для трассировки выполняемых функций C++. -
Установите пакет
adqm-clickhouse-debuginfo. Убедитесь, что настройкаallow_introspection_functionsактивирована на уровне пользователя, который будет подключаться к ADQM и использовать функциюflameGraph. Эту опцию также можно включать на уровне сессииclickhouse-client:-
в пакетном режиме:
$ clickhouse-client --allow_introspection_functions=1 -q "..." ... -
в интерактивном режиме:
SET allow_introspection_functions = 1;
-
-
Скачайте скрипт flamegraph.pl на хосты ADQM и дайте разрешение на его выполнение (команда
chmod +x). Предварительно проверьте, что на хостах установлен Perl.
Теперь можно использовать функцию flameGraph и утилиту flamegraph.pl, чтобы получить профиль конкретного запроса в виде flame-графика. Например, следующая команда создает flame-график (интерактивный SVG-файл с именем <flame_cpu>), визуализирующий трассировки стека по времени CPU для запроса с идентификатором <query_id> (<path_to> в данном примере — путь к скрипту flamegraph.pl на хосте ADQM):
$ clickhouse-client \
-q "SELECT arrayJoin(flameGraph(arrayReverse(trace))) \
FROM system.trace_log \
WHERE trace_type = 'CPU' AND query_id = '<query_id>'" \
| <path_to>/flamegraph.pl > <flame_cpu>.svg
Пример
Данный пример показывает, как с помощью flame-графика можно посмотреть, какие функции и в каком порядке вызываются при выполнении запроса.
-
Создайте таблицу без первичного ключа с двумя столбцами — данные в первом столбце будут сжиматься по алгоритму LZ4, второй столбец будет хранить данные без сжатия:
CREATE TABLE test_table (a Int64 CODEC(LZ4), b Int64 CODEC(NONE)) ENGINE = MergeTree ORDER BY tuple(); -
Вставьте в таблицу 100 миллионов строк с тестовыми значениями:
INSERT INTO test_table SELECT 1, 1 FROM numbers(100000000);С помощью следующего запроса можно посмотреть размер хранимых данных и увидеть разницу между столбцами:
SELECT name, formatReadableSize(data_uncompressed_bytes) AS uncompressed_size, formatReadableSize(data_compressed_bytes) AS compressed_size, round(data_uncompressed_bytes / data_compressed_bytes, 2) AS ratio FROM system.columns WHERE table = 'test_table';┌─name─┬─uncompressed_size─┬─compressed_size─┬─ratio─┐ 1. │ a │ 762.94 MiB │ 3.45 MiB │ 221.4 │ 2. │ b │ 762.94 MiB │ 763.23 MiB │ 1 │ └──────┴───────────────────┴─────────────────┴───────┘
-
Выполните два отдельных запроса для вычисления суммы значений в каждом столбце:
SELECT sum(a) FROM test_table;Query id: adde6caf-60c5-4820-8bb3-5f75a8edb98e ┌────sum(a)─┐ 1. │ 100000000 │ -- 100.00 million └───────────┘ 1 row in set. Elapsed: 0.072 sec. Processed 100.00 million rows, 800.00 MB (1.39 billion rows/s., 11.14 GB/s.) Peak memory usage: 7.92 MiB.
SELECT sum(b) FROM test_table;Query id: 8491a039-be0d-4a17-9130-ec286dfee039 ┌────sum(b)─┐ 1. │ 100000000 │ -- 100.00 million └───────────┘ 1 row in set. Elapsed: 0.363 sec. Processed 100.00 million rows, 800.00 MB (275.73 million rows/s., 2.21 GB/s.) Peak memory usage: 14.51 MiB.
-
Постройте flame-график по загрузке CPU для первого запроса:
$ clickhouse-client \ -q "SELECT arrayJoin(flameGraph(arrayReverse(trace))) \ FROM system.trace_log \ WHERE trace_type = 'CPU' AND query_id = 'adde6caf-60c5-4820-8bb3-5f75a8edb98e'" \ | flamegraph.pl > flamegraph_cpu_1.svgFlame-график загрузки CPU при вычислении суммы значений в столбце, для которого применяется кодек сжатия данныхНа flame-графике показан полный стек вызываемых функций C++. Например, можно увидеть, что в процессе чтения данных выполняется декомпрессия (вызываются функции
decompress). -
Постройте flame-график для второго запроса:
$ clickhouse-client \ -q "SELECT arrayJoin(flameGraph(arrayReverse(trace))) \ FROM system.trace_log \ WHERE trace_type = 'CPU' AND query_id = '8491a039-be0d-4a17-9130-ec286dfee039'" \ | flamegraph.pl > flamegraph_cpu_2.svgПо графику видно, что в данном случае также используется класс
CompressedReadBufferBase, но он уже не вызывает функции декомпрессии (так как данные считываются из столбца, где не применяется сжатие).Flame-график загрузки CPU для запроса чтения данных из столбца без декомпрессии