メインコンテンツへスキップ
メインコンテンツへスキップ

アロケーションプロファイリング

ClickHouse はグローバルアロケータとして jemalloc を使用しています。jemalloc にはアロケーションのサンプリングおよびプロファイリング用のツールが付属しています。

ClickHouse と Keeper では、設定ファイル、クエリ設定、SYSTEM コマンド、および Keeper の four letter word (4LW) コマンドを使用してサンプリングを制御できます。結果を確認する方法はいくつかあります:

  • system.trace_logJemallocSample 型としてサンプルを収集し、クエリ単位で分析する。
  • 組み込みの jemalloc web UI (26.2+) を通じて、リアルタイムのメモリ統計を表示し、ヒーププロファイルを取得する。
  • SQL から system.jemalloc_profile_text (26.2+) を使用して現在のヒーププロファイルを直接クエリする。
  • ヒーププロファイルをディスクにフラッシュし、jeprof で分析する。
注記

このガイドはバージョン 25.9 以降に適用されます。 それ以前のバージョンについては、25.9 より前のバージョン向けアロケーションプロファイリング を参照してください。

アロケーションのサンプリング

アロケーションのサンプリングおよびプロファイリングを行うには、jemalloc_enable_global_profiler 設定を有効にして ClickHouse/Keeper を起動します。

<clickhouse>
    <jemalloc_enable_global_profiler>1</jemalloc_enable_global_profiler>
</clickhouse>

jemalloc はアロケーションをサンプリングし、その情報を内部に保持します。

jemalloc_enable_profiler 設定を使用することで、クエリ単位のサンプリングを有効にすることもできます。

警告

ClickHouse はアロケーションが多いアプリケーションであるため、jemalloc のサンプリングによりパフォーマンス上のオーバーヘッドが発生する可能性があります。

system.trace_log に jemalloc サンプルを保存する

jemalloc サンプルを JemallocSample 型として system.trace_log に格納できます。 これをグローバルに有効化するには、jemalloc_collect_global_profile_samples_in_trace_log 設定を使用します。

<clickhouse>
    <jemalloc_collect_global_profile_samples_in_trace_log>1</jemalloc_collect_global_profile_samples_in_trace_log>
</clickhouse>
警告

ClickHouse はメモリ割り当てを多用するアプリケーションであるため、system.trace_log ですべてのサンプルを収集すると高負荷になる可能性があります。

jemalloc_collect_profile_samples_in_trace_log 設定を使用して、クエリごとに有効化することもできます。

例: クエリのメモリ使用量を分析する

まず、jemalloc プロファイラを有効にした状態でクエリを実行し、そのクエリのサンプルを system.trace_log に収集します。

SELECT *
FROM numbers(1000000)
ORDER BY number DESC
SETTINGS max_bytes_ratio_before_external_sort = 0
FORMAT `Null`
SETTINGS jemalloc_enable_profiler = 1, jemalloc_collect_profile_samples_in_trace_log = 1

Query id: 8678d8fe-62c5-48b8-b0cd-26851c62dd75

Ok.

0 rows in set. Elapsed: 0.009 sec. Processed 1.00 million rows, 8.00 MB (108.58 million rows/s., 868.61 MB/s.)
Peak memory usage: 12.65 MiB.
注記

ClickHouse を jemalloc_enable_global_profiler を有効にして起動した場合、jemalloc_enable_profiler を有効にする必要はありません。 jemalloc_collect_global_profile_samples_in_trace_logjemalloc_collect_profile_samples_in_trace_log についても同様です。

system.trace_log をフラッシュします。

SYSTEM FLUSH LOGS trace_log

そして、それに対してクエリを実行し、時間の経過に応じた累積メモリ使用量を取得します。

WITH per_bucket AS
(
    SELECT
        event_time_microseconds AS bucket_time,
        sum(size) AS bucket_sum
    FROM system.trace_log
    WHERE trace_type = 'JemallocSample'
      AND query_id = '8678d8fe-62c5-48b8-b0cd-26851c62dd75'
    GROUP BY bucket_time
)
SELECT
    bucket_time,
    sum(bucket_sum) OVER (
        ORDER BY bucket_time ASC
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS cumulative_size,
    formatReadableSize(cumulative_size) AS cumulative_size_readable
FROM per_bucket
ORDER BY bucket_time

メモリ使用量が最大となった時刻を特定します:

SELECT
    argMax(bucket_time, cumulative_size),
    max(cumulative_size)
FROM
(
    WITH per_bucket AS
    (
        SELECT
            event_time_microseconds AS bucket_time,
            sum(size) AS bucket_sum
        FROM system.trace_log
        WHERE trace_type = 'JemallocSample'
          AND query_id = '8678d8fe-62c5-48b8-b0cd-26851c62dd75'
        GROUP BY bucket_time
    )
    SELECT
        bucket_time,
        sum(bucket_sum) OVER (
            ORDER BY bucket_time ASC
            ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        ) AS cumulative_size,
        formatReadableSize(cumulative_size) AS cumulative_size_readable
    FROM per_bucket
    ORDER BY bucket_time
)

その結果を使って、ピーク時にどの割り当てスタックが最も活発だったかを確認できます。

SELECT
    concat(
        '\n',
        arrayStringConcat(
            arrayMap(
                (x, y) -> concat(x, ': ', y),
                arrayMap(x -> addressToLine(x), allocation_trace),
                arrayMap(x -> demangle(addressToSymbol(x)), allocation_trace)
            ),
            '\n'
        )
    ) AS symbolized_trace,
    sum(s) AS per_trace_sum
FROM
(
    SELECT
        ptr,
        sum(size) AS s,
        argMax(trace, event_time_microseconds) AS allocation_trace
    FROM system.trace_log
    WHERE trace_type = 'JemallocSample'
      AND query_id = '8678d8fe-62c5-48b8-b0cd-26851c62dd75'
      AND event_time_microseconds <= '2025-09-04 11:56:21.737139'
    GROUP BY ptr
    HAVING s > 0
)
GROUP BY ALL
ORDER BY per_trace_sum ASC

Jemalloc Web UI

注記

このセクションはバージョン 26.2 以降に適用されます。

ClickHouse には、/jemalloc HTTP エンドポイントで jemalloc メモリ統計情報を表示するための組み込みの Web UI が用意されています。 この UI では、割り当て済みメモリ、アクティブメモリ、常駐メモリ、マップ済みメモリに加えて、アリーナごとおよび bin ごとの統計情報を含むメモリメトリクスを、リアルタイムにグラフで表示します。 また、グローバルおよびクエリ単位のヒーププロファイルを UI から直接取得することもできます。

アクセスするには、ブラウザで次の URL を開きます。

http://localhost:8123/jemalloc

SQL からヒーププロファイルを取得する

注記

このセクションはバージョン 26.2 以降に適用されます。

system.jemalloc_profile_text システムテーブルを使用すると、外部ツールを利用したり、事前にディスクへフラッシュしたりすることなく、現在の jemalloc ヒーププロファイルを SQL から直接取得して表示できます。

このテーブルは 1 つのカラムのみを持ちます:

カラムTypeDescription
lineStringシンボル化された jemalloc ヒーププロファイルの 1 行です。

ヒーププロファイルを事前にフラッシュする必要はなく、このテーブルに対して直接クエリを実行できます:

SELECT * FROM system.jemalloc_profile_text

出力形式

出力形式は jemalloc_profile_text_output_format 設定によって制御され、次の3つの値を取ります。

  • raw — jemalloc によって生成される生のヒーププロファイル。
  • symbolized — 関数シンボルを埋め込んだ jeprof 互換の形式。シンボルがすでに埋め込まれているため、ClickHouse バイナリを用意しなくても jeprof で出力を解析できます。
  • collapsed (デフォルト) — FlameGraph 互換の折り畳みスタック形式。1 行につき 1 つのスタックと、そのスタックのバイト数が含まれます。

例えば、生のプロファイルを取得するには次のようにします。

SELECT * FROM system.jemalloc_profile_text
SETTINGS jemalloc_profile_text_output_format = 'raw'

シンボル情報付きの出力を得るには:

SELECT * FROM system.jemalloc_profile_text
SETTINGS jemalloc_profile_text_output_format = 'symbolized'

追加設定

  • jemalloc_profile_text_symbolize_with_inline (Bool, デフォルト: true) — シンボル化の際にインラインフレームを含めるかどうかを指定します。無効にするとシンボル化は大幅に高速になりますが、インライン化された関数呼び出しがスタックに現れなくなるため精度が低下します。symbolized および collapsed フォーマットにのみ影響します。
  • jemalloc_profile_text_collapsed_use_count (Bool, デフォルト: false) — collapsed フォーマットを使用する場合、バイト数ではなくアロケーション回数で集約します。

例: SQL からフレームグラフを生成する

デフォルトの出力形式は collapsed なので、その出力をそのまま FlameGraph にパイプできます:

clickhouse-client -q "SELECT * FROM system.jemalloc_profile_text" | flamegraph.pl --color=mem --title="Allocation Flame Graph" --width 2400 > result.svg

バイト数ではなく割り当て回数に基づいたフレームグラフを生成するには、次のようにします:

clickhouse-client -q "SELECT * FROM system.jemalloc_profile_text SETTINGS jemalloc_profile_text_collapsed_use_count = 1" | flamegraph.pl --color=mem --title="Allocation Count Flame Graph" --width 2400 > result.svg

ヒーププロファイルをディスクにフラッシュする

ヒーププロファイルを jeprof でオフライン解析するためにファイルとして保存する必要がある場合は、プロファイルをディスクにフラッシュできます。

デフォルトでは、ヒーププロファイル用ファイルは /tmp/jemalloc_clickhouse._pid_._seqnum_.heap に生成されます。ここで _pid_ は ClickHouse の PID、_seqnum_ は現在のヒーププロファイルに対応するグローバルなシーケンス番号です。 Keeper のデフォルトファイルは /tmp/jemalloc_keeper._pid_._seqnum_.heap で、同じルールに従います。

現在のプロファイルをフラッシュするには次を実行します。

SYSTEM JEMALLOC FLUSH PROFILE

フラッシュされたプロファイルの保存先パスが返されます。

MALLOC_CONF 環境変数に prof_prefix オプションを追加することで、別の保存場所を指定できます。 例えば、/data ディレクトリ内に、ファイル名のプレフィックスを my_current_profile としてプロファイルを生成したい場合は、次の環境変数を指定して ClickHouse/Keeper を実行します。

MALLOC_CONF=prof_prefix:/data/my_current_profile

生成されるファイル名は、プレフィックスに PID とシーケンス番号を連結したものになります。

jeprof を使用したヒーププロファイルファイルの分析

ヒーププロファイルをディスクにフラッシュした後は、jemalloc のツールである jeprof を使用して分析できます。次のいずれかの方法でインストールできます。

  • システムのパッケージマネージャーを使用する
  • jemalloc リポジトリをクローンし、ルートディレクトリで autogen.sh を実行する。これにより、bin ディレクトリ内に jeprof スクリプトが作成されます

利用可能な出力形式が多数用意されています。利用可能なオプションの一覧は、jeprof --help を実行して確認してください。

シンボル情報付きヒーププロファイル

バージョン 26.1 以降では、SYSTEM JEMALLOC FLUSH PROFILE を使用してフラッシュを実行すると、ClickHouse は自動的にシンボル情報付きのヒーププロファイルを生成します。 シンボル情報付きプロファイル(拡張子は .symbolized)には関数シンボルが埋め込まれており、ClickHouse のバイナリを用意しなくても jeprof で分析できます。

たとえば、次のように実行すると:

SYSTEM JEMALLOC FLUSH PROFILE

ClickHouse は、シンボル化されたプロファイルへのパスを返します(例: /tmp/jemalloc_clickhouse.12345.0.heap.symbolized)。

その後、これを jeprof で直接解析できます。

jeprof /tmp/jemalloc_clickhouse.12345.0.heap.symbolized --output_format [ > output_file]
注記

バイナリは不要です: シンボル化されたプロファイル(.symbolized ファイル)を使用する場合、jeprof に ClickHouse バイナリのパスを指定する必要はありません。これにより、別のマシン上で、あるいはバイナリを更新した後でも、プロファイルをはるかに簡単に分析できるようになります。

古いシンボル化されていないヒーププロファイルがあり、引き続き ClickHouse バイナリにアクセスできる場合は、従来の方法を使用できます。

jeprof path/to/clickhouse path/to/heap/profile --output_format [ > output_file]
注記

シンボル化されていないプロファイルの場合、jeprof はスタックトレースを生成するために addr2line を使用しますが、処理が非常に遅くなることがあります。 その場合は、このツールの代替実装をインストールすることを推奨します。

git clone https://github.com/gimli-rs/addr2line.git --depth=1 --branch=0.23.0
cd addr2line
cargo build --features bin --release
cp ./target/release/addr2line path/to/current/addr2line

また、llvm-addr2line も同様に問題なく動作します(ただし、llvm-objdumpjeprof とは互換性がない点に注意してください)。

その後、次のように指定して使用します: jeprof --tools addr2line:/usr/bin/llvm-addr2line,nm:/usr/bin/llvm-nm,objdump:/usr/bin/objdump,c++filt:/usr/bin/llvm-cxxfilt

:::

2つのプロファイルを比較する際には、--base 引数を使用できます。

jeprof --base /path/to/first.heap.symbolized /path/to/second.heap.symbolized --output_format [ > output_file]

シンボル化されたプロファイルを使用する(推奨):

  • 各プロシージャを 1 行に 1 つずつ記載したテキストファイルを生成します:
jeprof /tmp/jemalloc_clickhouse.12345.0.heap.symbolized --text > result.txt
  • コールグラフを含む PDF ファイルを生成する:
jeprof /tmp/jemalloc_clickhouse.12345.0.heap.symbolized --pdf > result.pdf

シンボル化されていないプロファイルを使用する場合(バイナリが必要):

  • 各関数を 1 行ずつ記述したテキストファイルを生成します。
jeprof /path/to/clickhouse /tmp/jemalloc_clickhouse.12345.0.heap --text > result.txt
  • コールグラフを含む PDF ファイルを生成する:
jeprof /path/to/clickhouse /tmp/jemalloc_clickhouse.12345.0.heap --pdf > result.pdf

フレームグラフの生成

jeprof を使用すると、フレームグラフの作成に必要なコラプスされたスタック(collapsed stacks)を生成できます。

--collapsed 引数を使用する必要があります。

jeprof /tmp/jemalloc_clickhouse.12345.0.heap.symbolized --collapsed > result.collapsed

あるいは、シンボル情報のないプロファイルの場合:

jeprof /path/to/clickhouse /tmp/jemalloc_clickhouse.12345.0.heap --collapsed > result.collapsed

その後は、コラプスされたスタックを可視化するために利用できるツールが多数あります。

最も広く使われているのは FlameGraph で、flamegraph.pl というスクリプトが含まれています。

cat result.collapsed | /path/to/FlameGraph/flamegraph.pl --color=mem --title="Allocation Flame Graph" --width 2400 > result.svg

もう 1 つ便利なツールとして speedscope があり、収集したスタックをよりインタラクティブに分析できます。

プロファイラ用の追加オプション

jemalloc にはプロファイラに関連する多くのオプションがあり、MALLOC_CONF 環境変数を変更することで制御できます。 例えば、メモリ割り当てサンプル間の間隔は lg_prof_sample で制御できます。
ヒーププロファイルを N バイトごとにダンプしたい場合は、lg_prof_interval を有効にします。

利用可能なオプションの完全な一覧については、jemallocリファレンスページ を参照してください。

その他のリソース

ClickHouse/Keeper は、jemalloc 関連のメトリクスをさまざまな方法で公開します。

警告

これらのメトリクスは相互に同期されておらず、値がずれる可能性があることを認識しておくことが重要です。

システムテーブル asynchronous_metrics

SELECT *
FROM system.asynchronous_metrics
WHERE metric LIKE '%jemalloc%'
FORMAT Vertical

リファレンス

システムテーブル jemalloc_bins

すべてのアリーナから集約された、さまざまなサイズクラス(bin)における jemalloc アロケータによるメモリ割り当てに関する情報を含みます。

リファレンス

システムテーブル jemalloc_stats (26.2+)

malloc_stats_print() の出力全体を単一の文字列として返します。SYSTEM JEMALLOC STATS コマンドと同等です。

SELECT * FROM system.jemalloc_stats

Prometheus

asynchronous_metrics に含まれるすべての jemalloc 関連メトリクスは、ClickHouse と Keeper の両方で Prometheus エンドポイントからも公開されます。

リファレンス

Keeper における jmst 4LW コマンド

Keeper は jmst 4LW コマンドをサポートしており、基本的なアロケータ統計情報を返します。

echo jmst | nc localhost 9181