> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-revert-104359-revert-104251-parquet-single.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Документация по инструменту выборочного профилировщика запросов в ClickHouse

# Выборочный профилировщик запросов

В ClickHouse работает выборочный профилировщик, который позволяет анализировать выполнение запросов.
С его помощью можно найти процедуры в исходном коде, которые чаще всего используются во время выполнения запроса.
Вы можете отслеживать затраченное время CPU и реальное время, включая время простоя.

Профилировщик запросов автоматически включен в ClickHouse Cloud.
Следующий пример запроса находит наиболее частые трассировки стека для профилируемого запроса с разрешенными именами функций и указанием их местоположения в исходном коде:

По умолчанию профилировщик символизирует трассировки стека во время сбора и сохраняет результаты в столбцах `symbols` и `lines` таблицы [`system.trace_log`](/ru/reference/system-tables/trace_log), поэтому приведенные ниже примеры считывают эти столбцы напрямую и не требуют функций интроспекции. Символизация управляется настройкой `symbolize` в разделе конфигурации сервера `trace_log` (включена по умолчанию) и поддерживается на платформах ELF (например, Linux) и macOS; в FreeBSD столбцы `symbols` и `lines` всегда пусты. Имена функций в `symbols` берутся из таблицы символов бинарного файла и доступны по умолчанию. Местоположения в исходном коде в `lines` определяются по мере возможности: для них требуется отладочная информация (в macOS — пакет `.dSYM` рядом с бинарным файлом), а на платформах ELF разрешаются только кадры внутри основного бинарного файла ClickHouse, поэтому записи для кадров, которые не удается разрешить (например, в общих библиотеках), остаются пустыми. Если символизация отключена, используйте [функции интроспекции](/ru/reference/functions/regular-functions/introspection) `addressToSymbol`, `demangle` и `addressToLine`, чтобы вместо этого разрешить необработанные адреса в столбце `trace`. Эти функции доступны на тех же платформах, что и символизация (платформы ELF, такие как Linux, и macOS); в FreeBSD они также не скомпилированы, поэтому адреса в `trace` необходимо разрешать вне сервера.

<Tip>
  Замените значение `query_id` на ID запроса, который вы хотите профилировать.
</Tip>

<Tabs>
  <Tab title="ClickHouse Cloud">
    В ClickHouse Cloud ID запроса можно получить, нажав **"..."** в крайней правой части панели над таблицей результатов запроса (рядом с переключателем table/chart). Откроется контекстное меню, в котором можно нажать **"Copy query ID"**.

    Используйте `clusterAllReplicas(default, system.trace_log)`, чтобы выбрать данные со всех узлов cluster:

    ```sql theme={null}
    SELECT
        count(),
        arrayStringConcat(arrayMap((symbol, line) -> concat(symbol, '\n    ', line), any(symbols), any(lines)), '\n') AS sym
    FROM clusterAllReplicas(default, system.trace_log)
    WHERE query_id = '<query_id>' AND trace_type = 'CPU' AND event_date = today()
    GROUP BY trace
    ORDER BY count() DESC
    LIMIT 10
    ```
  </Tab>

  <Tab title="Самоуправляемый">
    ```sql theme={null}
    SELECT
        count(),
        arrayStringConcat(arrayMap((symbol, line) -> concat(symbol, '\n    ', line), any(symbols), any(lines)), '\n') AS sym
    FROM system.trace_log
    WHERE query_id = '<query_id>' AND trace_type = 'CPU' AND event_date = today()
    GROUP BY trace
    ORDER BY count() DESC
    LIMIT 10
    ```
  </Tab>
</Tabs>

<div id="self-managed-query-profiler">
  ## Использование профилировщика запросов в самоуправляемых развертываниях
</div>

Чтобы использовать профилировщик запросов в самоуправляемых развертываниях, выполните следующие шаги:

<Steps>
  <Step title="Установите ClickHouse с отладочной информацией" id="debug-info">
    Установите пакет `clickhouse-common-static-dbg`:

    1. Следуйте инструкциям из шага ["Настройте Debian-репозиторий"](/ru/get-started/setup/self-managed/debian-ubuntu#setup-the-debian-repository)
    2. Выполните `sudo apt-get install clickhouse-server clickhouse-client clickhouse-common-static-dbg`, чтобы установить скомпилированные бинарные файлы ClickHouse с отладочной информацией
    3. Выполните `sudo service clickhouse-server start`, чтобы запустить сервер
    4. Выполните `clickhouse-client`. Отладочные символы из `clickhouse-common-static-dbg` будут автоматически подхвачены сервером — ничего дополнительно включать не нужно
  </Step>

  <Step title="Проверьте конфигурацию сервера" id="server-config">
    Убедитесь, что раздел [`trace_log`](/ru/reference/settings/server-settings/settings/other#trace_log) в вашем [файле конфигурации сервера](/ru/concepts/features/configuration/server-config/configuration-files) настроен. По умолчанию он включен:

    ```xml theme={null}
    <!-- Trace log. Stores stack traces collected by query profilers.
         See query_profiler_real_time_period_ns and query_profiler_cpu_time_period_ns settings. -->
    <trace_log>
        <database>system</database>
        <table>trace_log</table>

        <partition_by>toYYYYMM(event_date)</partition_by>
        <flush_interval_milliseconds>7500</flush_interval_milliseconds>
        <max_size_rows>1048576</max_size_rows>
        <reserved_size_rows>8192</reserved_size_rows>
        <buffer_size_rows_flush_threshold>524288</buffer_size_rows_flush_threshold>
        <!-- Indication whether logs should be dumped to the disk in case of a crash -->
        <flush_on_crash>false</flush_on_crash>
        <symbolize>true</symbolize>
    </trace_log>
    ```

    Этот раздел настраивает системную таблицу [trace\_log](/ru/reference/system-tables/trace_log), содержащую результаты работы профилировщика.
    Параметр `symbolize` (включен по умолчанию) позволяет ClickHouse разрешать каждый кадр стека во время сбора и сохранять деманглированные имена функций и расположения в исходном коде в столбцах `symbols` и `lines`.
    Имена функций в `symbols` берутся из таблицы символов и доступны по умолчанию, тогда как расположения в исходном коде в `lines` требуют отладочной информации (пакета `.dSYM` в macOS) и на платформах ELF разрешаются только для кадров внутри основного бинарного файла ClickHouse; для неразрешённых кадров записи в `lines` остаются пустыми.

    Обратите внимание, что исходные адреса в столбце `trace` менее стабильны при перезапусках и обновлениях, чем предварительно символизированные столбцы.
    На платформах ELF, кроме FreeBSD, кадры в основном бинарном файле ClickHouse хранятся как физические смещения в файле, поэтому остаются разрешимыми после перезапуска, пока бинарный файл не изменён; в macOS и FreeBSD они хранятся как виртуальные адреса времени выполнения, которые могут стать недействительными после перезапуска.
    Кадры за пределами основного бинарного файла (например, в разделяемых библиотеках) всегда хранятся как виртуальные адреса времени выполнения, которые могут стать недействительными после перезапуска, а любой исходный адрес становится неразрешимым после обновления бинарного файла из-за изменения структуры кода.
    ClickHouse не очищает таблицу при перезапуске, поэтому устаревшие исходные адреса могут сохраняться.
    Предварительно символизированные столбцы `symbols` и `lines`, напротив, остаются действительными после перезапусков и обновлений, поэтому при анализе исторических данных предпочтительно использовать их.
  </Step>

  <Step title="Настройте таймеры профилирования" id="configure-profile-timers">
    Настройте параметры [`query_profiler_cpu_time_period_ns`](/ru/reference/settings/session-settings/query-profiler#query_profiler_cpu_time_period_ns) или [`query_profiler_real_time_period_ns`](/ru/reference/settings/session-settings/query-profiler#query_profiler_real_time_period_ns).
    Оба параметра можно использовать одновременно.

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

    Частота сэмплирования по умолчанию — один сэмпл в секунду, при этом включены таймеры CPU и реального времени.
    Такая частота позволяет собирать достаточно информации о кластере ClickHouse, не влияя на производительность сервера.
    Если вам нужно профилировать каждый отдельный запрос, используйте более высокую частоту сэмплирования.
  </Step>

  <Step title={<>Проанализируйте системную таблицу <code>trace_log</code></>} id="analyze-trace-log-system-table">
    Чтобы получить профиль для какого-либо запроса, нужно агрегировать данные из таблицы `trace_log`.
    Вы можете агрегировать данные по отдельным функциям или по всей трассировке стека.

    Когда символизация включена (по умолчанию), деманглированные имена функций и расположения в исходном коде уже доступны в столбцах `symbols` и `lines`, поэтому дополнительная настройка не требуется. Символизация не поддерживается в FreeBSD, где эти столбцы всегда пусты. Записи `lines` могут быть пустыми для кадров без отладочной информации или находящихся за пределами основного бинарного файла ClickHouse (см. [выше](#server-config)).

    Если символизация отключена или вы хотите динамически разрешать необработанные адреса в столбце `trace` (например, чтобы развернуть встроенные кадры), разрешите функции интроспекции с помощью настройки [`allow_introspection_functions`](/ru/reference/settings/session-settings/allow#allow_introspection_functions):

    ```sql theme={null}
    SET allow_introspection_functions=1
    ```

    <Note>
      По соображениям безопасности функции интроспекции по умолчанию отключены
    </Note>

    Используйте [функции интроспекции](/ru/reference/functions/regular-functions/introspection) `addressToLine`, `addressToLineWithInlines`, `addressToSymbol` и `demangle`, чтобы получить имена функций и их расположение в коде ClickHouse. Как и символизация, эти функции доступны на платформах ELF (таких как Linux) и macOS, но недоступны в FreeBSD.

    <Tip>
      Если вам нужно визуализировать данные из `trace_log`, попробуйте [флеймграф](/ru/integrations/connectors/tools/gui#clickhouse-flamegraph) и [speedscope](https://www.speedscope.app).
    </Tip>
  </Step>
</Steps>

<div id="flamegraph">
  ## Построение флеймграфов с помощью функции `flameGraph`
</div>

ClickHouse предоставляет агрегатную функцию [`flameGraph`](/ru/reference/functions/aggregate-functions/flame_graph), которая строит флеймграф напрямую по трассировкам стека, хранящимся в `trace_log`.
На выходе получается массив строк в формате, совместимом с [flamegraph.pl](https://github.com/brendangregg/FlameGraph).

**Синтаксис:**

```sql theme={null}
flameGraph(traces, [size = 1], [ptr = 0])
```

**Аргументы:**

* `traces` — стектрейс. [`Array(UInt64)`](/ru/reference/data-types/array).
* `size` — размер выделения для профилирования памяти. [`Int64`](/ru/reference/data-types/int-uint).
* `ptr` — адрес выделенной памяти. [`UInt64`](/ru/reference/data-types/int-uint).

Если `ptr` не равен нулю, `flameGraph` сопоставляет выделения (`size > 0`) и освобождения (`size < 0`) с одинаковыми размером и указателем.
Отображаются только выделения, которые не были освобождены.
Несопоставленные освобождения игнорируются.

<div id="cpu-flame-graph">
  ### CPU-флеймграф
</div>

<Note>
  Для выполнения приведённых ниже запросов у вас должен быть установлен [flamegraph.pl](https://github.com/brendangregg/FlameGraph).

  Для этого выполните:

  ```bash theme={null}
  git clone https://github.com/brendangregg/FlameGraph
  # Затем используйте его так:
  # ~/FlameGraph/flamegraph.pl
  ```

  В следующих запросах замените `flamegraph.pl` на путь к файлу `flamegraph.pl` на вашей машине
</Note>

```sql theme={null}
SET query_profiler_cpu_time_period_ns = 10000000;
```

Выполните запрос, затем постройте флеймграф:

```bash theme={null}
clickhouse client --allow_introspection_functions=1 \
    -q "SELECT arrayJoin(flameGraph(arrayReverse(trace)))
        FROM system.trace_log
        WHERE trace_type = 'CPU' AND query_id = '<query_id>'" \
    | flamegraph.pl > flame_cpu.svg
```

<div id="memory-flame-graph-all">
  ### Флеймграф памяти — все выделения
</div>

```sql theme={null}
SET memory_profiler_sample_probability = 1, max_untracked_memory = 1;
```

Выполните запрос, затем постройте флеймграф:

```bash theme={null}
clickhouse client --allow_introspection_functions=1 \
    -q "SELECT arrayJoin(flameGraph(trace, size))
        FROM system.trace_log
        WHERE trace_type = 'MemorySample' AND query_id = '<query_id>'" \
    | flamegraph.pl --countname=bytes --color=mem > flame_mem.svg
```

<div id="memory-flame-graph-unfreed">
  ### Флеймграф памяти — неосвобождённые выделения
</div>

Этот вариант сопоставляет выделения памяти с освобождениями по указателю и показывает только ту память, которая не была освобождена во время запроса.

```sql theme={null}
SET memory_profiler_sample_probability = 1, max_untracked_memory = 1,
    use_uncompressed_cache = 1,
    merge_tree_max_rows_to_use_cache = 100000000000,
    merge_tree_max_bytes_to_use_cache = 1000000000000;
```

Выполните следующий запрос, чтобы построить флеймграф:

```bash theme={null}
clickhouse client --allow_introspection_functions=1 \
    -q "SELECT arrayJoin(flameGraph(trace, size, ptr))
        FROM system.trace_log
        WHERE trace_type = 'MemorySample' AND query_id = '<query_id>'" \
    | flamegraph.pl --countname=bytes --color=mem > flame_mem_unfreed.svg
```

<div id="memory-flame-graph-time-point">
  ### Флеймграф памяти — активные выделения на определённый момент времени
</div>

Этот подход позволяет определить пиковое потребление памяти и визуализировать, что было выделено в этот момент.

```sql theme={null}
SET memory_profiler_sample_probability = 1, max_untracked_memory = 1;
```

<div id="find-memory-usage-over-time">
  #### Определите использование памяти во времени
</div>

```sql theme={null}
SELECT
    event_time,
    formatReadableSize(max(s)) AS m
FROM (
    SELECT
        event_time,
        sum(size) OVER (ORDER BY event_time) AS s
    FROM system.trace_log
    WHERE query_id = '<query_id>' AND trace_type = 'MemorySample'
)
GROUP BY event_time
ORDER BY event_time;
```

<div id="find-time-point-maximum-memory-usage">
  #### Найдите момент времени, в который использование памяти было максимальным
</div>

```sql theme={null}
SELECT
    argMax(event_time, s),
    max(s)
FROM (
    SELECT
        event_time,
        sum(size) OVER (ORDER BY event_time) AS s
    FROM system.trace_log
    WHERE query_id = '<query_id>' AND trace_type = 'MemorySample'
);
```

<div id="build-flame-graph">
  #### Постройте флеймграф активных выделений в этот момент времени
</div>

```bash theme={null}
clickhouse client --allow_introspection_functions=1 \
    -q "SELECT arrayJoin(flameGraph(trace, size, ptr))
        FROM (
            SELECT * FROM system.trace_log
            WHERE trace_type = 'MemorySample'
              AND query_id = '<query_id>'
              AND event_time <= '<time_point>'
            ORDER BY event_time
        )" \
    | flamegraph.pl --countname=bytes --color=mem > flame_mem_time_point_pos.svg
```

<div id="build-flame-graph-deallocations">
  #### Постройте флеймграф освобождений памяти после этого момента времени (чтобы понять, что было освобождено позже)
</div>

```bash theme={null}
clickhouse client --allow_introspection_functions=1 \
    -q "SELECT arrayJoin(flameGraph(trace, -size, ptr))
        FROM (
            SELECT * FROM system.trace_log
            WHERE trace_type = 'MemorySample'
              AND query_id = '<query_id>'
              AND event_time > '<time_point>'
            ORDER BY event_time DESC
        )" \
    | flamegraph.pl --countname=bytes --color=mem > flame_mem_time_point_neg.svg
```

<div id="example">
  ## Пример
</div>

Приведённый ниже фрагмент кода:

* Фильтрует данные `trace_log` по идентификатору запроса и текущей дате.
* Считывает предварительно символизированные столбцы `symbols` и `lines`, чтобы сформировать отчёт со следующей информацией:
  * Имена символов и соответствующих им функций исходного кода.
  * Местоположения этих функций в исходном коде.
* Выполняет агрегацию по исходной трассировке стека (столбцу `trace`), используя символизированные столбцы только для отображения, чтобы разные трассировки стека никогда не объединялись из-за приближённой символизации.

```sql theme={null}
SELECT
    count(),
    arrayStringConcat(arrayMap((symbol, line) -> concat(symbol, '\n    ', line), any(symbols), any(lines)), '\n') AS sym
FROM system.trace_log
WHERE (query_id = '<query_id>') AND (event_date = today())
GROUP BY trace
ORDER BY count() DESC
LIMIT 10
```
