跳转到主要内容
ClickHouse 使用 jemalloc 作为全局 allocator。Jemalloc 自带用于分配采样和剖析的工具。 ClickHouse 和 Keeper 允许你使用 config、查询 settings、SYSTEM 命令以及 Keeper 中的 four letter word (4LW) 命令来控制采样。你可以通过以下几种方式查看结果:
  • 将 samples 收集到 system.trace_log 中,类型为 JemallocSample,以便进行按查询分析。
  • 通过内置的 jemalloc web UI 查看实时内存统计信息,并拉取堆内存剖析 (26.2+) 。
  • 使用 SQL 通过 system.jemalloc_profile_text 直接查询当前堆内存剖析 (26.2+) 。
  • 将堆内存剖析刷新到 disk,并使用 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 中收集所有 samples 可能会造成较高负载。
你也可以通过 jemalloc_collect_profile_samples_in_trace_log 设置为单个查询启用此功能。

示例:分析查询的内存使用情况

首先,在启用 jemalloc Profiler 的情况下运行查询,并将 samples 收集到 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_profilerjemalloc_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 端点提供了一个内置的 web UI,用于查看 jemalloc 内存统计信息。 它通过图表展示实时内存指标,包括 allocated、active、resident 和 mapped memory,以及按 arena 和 bin 划分的统计信息。 你还可以直接在 UI 中拉取全局和按查询划分的堆内存剖析。
http://localhost:8123/jemalloc
服务器 UI 包含所有选项卡:Summary、Allocations、Arenas、Operations、Global Profiler、Query Profiler 和 Raw Output。

从 SQL 获取堆内存剖析

本节适用于 26.2 及以上版本。
system.jemalloc_profile_text 系统表可让您直接通过 SQL 获取并查看当前的 jemalloc 堆内存剖析,无需先借助外部工具,也不必先将其落盘。 该表只有一列:
ColumnTypeDescription
lineString符号化后的 jemalloc 堆内存剖析中的一行。
您可以直接查询该表——无需预先落盘堆内存剖析:
SELECT * FROM system.jemalloc_profile_text

输出格式

输出格式由 jemalloc_profile_text_output_format 设置控制,它支持三个值:
  • raw — jemalloc 生成的原始堆内存剖析。
  • symbolized — 与 jeprof 兼容的格式,内嵌函数符号。由于符号已内嵌,jeprof 无需 ClickHouse 可执行文件即可分析输出。
  • collapsed (默认) — 与 FlameGraph 兼容的折叠栈格式,每行一个调用栈,并附带字节数。
例如,要获取原始剖析:
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, default: true) — 符号化时是否包含内联窗口帧。禁用此项可显著加快符号化速度,但会损失精度,因为内联函数调用将不会出现在堆栈中。仅影响 symbolizedcollapsed 格式。
  • jemalloc_profile_text_collapsed_use_count (Bool, default: 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,规则相同。 要刷写当前 profile:
SYSTEM JEMALLOC FLUSH PROFILE
该命令会返回已刷写 profile 的位置。
你也可以通过在 MALLOC_CONF 环境变量中附加 prof_prefix 选项来指定其他位置。 例如,如果你想在 /data 目录中生成 profile,并将文件名前缀设为 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+ 版本起,ClickHouse 会在你使用 SYSTEM JEMALLOC FLUSH PROFILE 执行 flush 时自动生成符号化的堆内存剖析。 符号化的堆内存剖析 (带有 .symbolized 扩展名) 包含已嵌入的函数符号,因此可由 jeprof 直接分析,而无需 ClickHouse 可执行文件。 例如,当你运行:
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]
对于未符号化的 profile,jeprof 会使用 addr2line 生成 stacktrace,这个过程可能会非常慢。 如果遇到这种情况,建议安装该工具的另一种实现
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
比较两个 profile 时,可以使用 --base 参数:
jeprof --base /path/to/first.heap.symbolized /path/to/second.heap.symbolized --output_format [ > output_file]

示例

使用符号化的堆内存剖析 (推荐) :
  • 生成一个文本文件,每行一个过程:
jeprof /tmp/jemalloc_clickhouse.12345.0.heap.symbolized --text > result.txt
  • 生成包含调用图的 PDF 文件:
jeprof /tmp/jemalloc_clickhouse.12345.0.heap.symbolized --pdf > result.pdf
使用未符号化的堆内存剖析 (需要二进制可执行文件) :
  • 生成一个文本文件,每行写入一个函数:
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 参数:
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
另一个有趣的工具是 speedscope,它可以让你以更交互的方式分析已收集的调用栈。

Profiler 的其他选项

jemalloc 提供了许多与 Profiler 相关的选项,这些选项可通过修改 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

包含从所有 arena 汇总而来的、通过 jemalloc 分配器在不同大小类 (bins) 中的内存分配信息。 参考

系统表 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
最后修改于 2026年6月10日