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进行分析。
内存分配采样
要对内存分配进行采样和分析,请先启用 jemalloc_enable_global_profiler 配置项,然后启动 ClickHouse/Keeper:
<clickhouse>
<jemalloc_enable_global_profiler>1</jemalloc_enable_global_profiler>
</clickhouse>jemalloc 会对内存分配进行采样,并在内部存储相关信息。
你也可以使用 jemalloc_enable_profiler 设置,为每个查询启用采样。
在 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>你也可以通过 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.刷新 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 ASCJemalloc web UI
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。
http://localhost:9182/jemallocKeeper UI 可通过 HTTP 控制端口访问。该端口默认禁用,必须在 Keeper 配置中显式设置 keeper_server.http_control.port 才能启用:
<clickhouse>
<keeper_server>
<http_control>
<port>9182</port>
</http_control>
</keeper_server>
</clickhouse>启用后,该 UI 提供与服务器相同的可视化内容 —— Summary、Allocations、Arenas、Operations、Global Profiler 和 Raw Output —— 但不包括 Query Profiler 选项卡,因为它需要 SQL 和 system.trace_log。
Keeper 还公开了可供程序化访问的 REST API 端点:
GET /jemalloc/stats— 原始malloc_stats_print输出GET /jemalloc/status— 以 JSON 返回剖析状态 (prof_enabled、prof_active、thread_active_init、lg_sample)GET /jemalloc/profile?format={collapsed|raw}— 刷写堆内存剖析并执行 server-side 符号化,返回适合 flame graph 渲染的 collapsed stacks (默认) 或原始 jemalloc 转储
从 SQL 获取堆内存剖析
system.jemalloc_profile_text 系统表可让您直接通过 SQL 获取并查看当前的 jemalloc 堆内存剖析,无需先借助外部工具,也不必先将其落盘。
该表只有一列:
| Column | Type | Description |
|---|---|---|
line |
String | 符号化后的 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) — 符号化时是否包含内联窗口帧。禁用此项可显著加快符号化速度,但会损失精度,因为内联函数调用将不会出现在堆栈中。仅影响symbolized和collapsed格式。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 的位置。
echo jmfp | nc localhost 9181你也可以通过在 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 PROFILEClickHouse 将返回符号化的堆内存剖析的路径 (例如 /tmp/jemalloc_clickhouse.12345.0.heap.symbolized) 。
然后,您可以直接使用 jeprof 对其进行分析:
jeprof /tmp/jemalloc_clickhouse.12345.0.heap.symbolized --output_format [ > output_file]如果你有较旧的未符号化堆内存剖析文件,并且仍可访问 ClickHouse 二进制文件,则可以使用传统方法:
jeprof path/to/clickhouse path/to/heap/profile --output_format [ > output_file]比较两个 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_statsPrometheus
asynchronous_metrics 中所有与 jemalloc 相关的指标,也会通过 ClickHouse 和 Keeper 的 Prometheus 端点公开。
Keeper 中的 jmst 4LW 命令
Keeper 支持 jmst 4LW 命令,用于返回基础分配器统计信息:
echo jmst | nc localhost 9181