[go: up one dir, main page]

メインコンテンツまでスキップ
メインコンテンツまでスキップ

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

ClickHouseはグローバルアロケータとして jemalloc を使用しています。 Jemallocにはアロケーションサンプリングとプロファイリングのためのツールがいくつか備わっています。
アロケーションプロファイリングをより便利にするために、ClickHouseとKeeperは設定、クエリ設定、SYSTEMコマンド、およびKeeperの4文字コマンド(4LW)を使用してサンプリングを制御することを可能にしています。
また、サンプルはsystem.trace_logテーブルのJemallocSampleタイプに収集することができます。

注記

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

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

jemallocでアロケーションをサンプリングしプロファイリングするには、設定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設定を使用することで、クエリごとに有効にすることもできます。

system.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

ヒーププロファイルのフラッシュ

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

現在のプロファイルをフラッシュするには、jemallocに次のコマンドを実行します:

SYSTEM JEMALLOC FLUSH PROFILE

フラッシュされたプロファイルの場所が返されます。

prof_prefixオプションを付加したMALLOC_CONF環境変数を追加することで、異なる場所を定義できます。
例えば、ファイル名のプレフィックスをmy_current_profileにして/dataフォルダにプロファイルを生成したい場合、次の環境変数でClickHouse/Keeperを実行します:

MALLOC_CONF=prof_prefix:/data/my_current_profile

生成されたファイルは、プレフィックスPIDとシーケンス番号に追加されます。

ヒーププロファイルの分析

ヒーププロファイルが生成されたら、それを分析する必要があります。
そのために、jemallocのツールである jeprof を使用することができます。これは複数の方法でインストールできます:

  • システムのパッケージマネージャを使用
  • jemallocリポジトリをクローンし、ルートフォルダからautogen.shを実行します。これにより、binフォルダ内にjeprofスクリプトが提供されます。
注記

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も同様に機能します。

jeprofを使用してヒーププロファイルから生成するフォーマットは多くあります。 ツールの使用法やさまざまなオプションについての情報は、jeprof --helpを実行することをお勧めします。

一般的に、jeprofコマンドは次のように使用されます:

jeprof path/to/binary path/to/heap/profile --output_format [ > output_file]

2つのプロファイル間でどのアロケーションが発生したかを比較したい場合は、base引数を設定できます:

jeprof path/to/binary --base path/to/first/heap/profile path/to/second/heap/profile --output_format [ > output_file]

  • 各手続きが1行ずつ書かれたテキストファイルを生成したい場合:
jeprof path/to/binary path/to/heap/profile --text > result.txt
  • コールグラフを持つPDFファイルを生成したい場合:
jeprof path/to/binary path/to/heap/profile --pdf > result.pdf

ファイアフレームグラフの生成

jeprofはファイアフレームグラフを構築するために圧縮スタックを生成することができます。

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

jeprof path/to/binary path/to/heap/profile --collapsed > result.collapsed

その後、圧縮スタックを視覚化するために多くの異なるツールを使用できます。

最も人気のあるツールは、FlameGraphで、flamegraph.plというスクリプトを備えています:

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

もう一つの興味深いツールは、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

異なるサイズクラス(ビン)においてjemallocアロケータを介して行われたメモリアロケーションに関する情報を、すべてのアリーナから集約して含みます。

リファレンス

Prometheus

asynchronous_metricsからのすべてのjemalloc関連メトリクスは、ClickHouseとKeeperの両方でPrometheusエンドポイントを使用して公開されています。

リファレンス

Keeperのjmst 4LWコマンド

Keeperは、基本アロケータ統計を返すjmst 4LWコマンドをサポートしています:

echo jmst | nc localhost 9181