解析 ClickHouse v23.11.5.29-stable 版本变更:Jemalloc 内存诊断命令与 Iceberg 引擎新设置
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
本篇围绕 ClickHouse 2023 年 11 月的一个稳定分支修订版 v23.11.5.29-stable(构建提交 d83b108deca,相对上一稳定版 v23.11.4.24-stable)的官方 changelog 展开。这个版本本身没有大规模功能新增,但集中补强了三块对生产运维有价值的东西:SYSTEM JEMALLOC系列内存诊断命令、Keeper 侧对应的 4LW 诊断命令,以及 Iceberg 表引擎的iceberg_engine_ignore_schema_evolution设置;同时修复了 7 个影响面较大的用户可见 Bug。读完本文,你可以掌握该版本新增命令的用法与底层实现、Iceberg 读取策略的取舍,以及每条修复所对应的问题域,便于评估是否值得升级到该稳定版。
一、版本定位:稳定分支上的“补丁包”式发布
v23.11.5.29-stable 是 v23.11 稳定分支上的一个修订版。从 changelog 结构看,它的两条 Improvement 均标注了 “Backported in ...”,说明这两个特性原本先合入主线(分别对应 PR #58665 与 #59133),随后被反向移植(backport)到稳定分支。这类修订版的典型特征是:不加新的大功能,只引入风险可控的增强和 Bug 修复,适合作为生产环境的小版本升级目标。
二、Improvement 1:SYSTEM JEMALLOC命令族——把 jemalloc 诊断能力暴露给 SQL
2.1 版本中新增的命令
该版本为使用 jemalloc 构建的 ClickHouse 新增了如下 SQL 命令(详见 changelog 的 Improvement 第一条,对应 PR #58665):
| 命令 | 作用 |
|---|---|
SYSTEM JEMALLOC PURGE | 向 jemalloc 发出 arena purge 请求,把不再使用的内存页归还给操作系统,降低 RSS 占用 |
SYSTEM JEMALLOC ENABLE PROFILE | 在 profiler 已编译启用的前提下,打开 jemalloc 堆采样剖析 |
SYSTEM JEMALLOC DISABLE PROFILE | 关闭堆采样剖析 |
SYSTEM JEMALLOC FLUSH PROFILE | 立即把当前堆剖析数据落盘,供后续符号化分析 |
配合这些命令,运维侧第一次可以直接用 SQL 回答两个常见问题:“ClickHouse 为什么不还内存了?”(用 PURGE 触发归还)和“内存都分配在哪了?”(用 ENABLE PROFILE → 跑负载 → FLUSH PROFILE 拿到堆剖析文件)。
2.2 源码层面的实现印证
结合当前仓库源码,可以看到这套命令的落地方式:
权限模型上,这四条命令被归入同一个访问类型。AccessType.h 中定义:
M(SYSTEM_JEMALLOC, "SYSTEM JEMALLOC PURGE, SYSTEM JEMALLOC ENABLE PROFILE, SYSTEM JEMALLOC DISABLE PROFILE, SYSTEM JEMALLOC FLUSH PROFILE", GLOBAL, SYSTEM) \即执行这些命令需要
GLOBAL级别的SYSTEM权限,属于典型的“高权限运维操作”。执行路径在 InterpreterSystemQuery.cpp 中。
JEMALLOC_PURGE分支先做checkAccess(AccessType::SYSTEM_JEMALLOC),然后调用Jemalloc::purgeArenas();JEMALLOC_FLUSH_PROFILE分支会调用Jemalloc::flushProfile("/tmp/jemalloc_clickhouse"),随后根据会话设置生成.symbolized或.collapsed后缀的符号化产物,并把输出文件路径作为查询结果返回。剖析产物格式由会话设置控制(Settings.cpp 中的声明):
jemalloc_profile_text_output_format:取值raw(原始 profile)、symbolized(jeprof 格式带符号)或collapsed(FlameGraph 折叠栈格式),默认collapsed;jemalloc_profile_text_symbolize_with_inline:是否包含 inline 帧,默认开启,关闭可换取更快的符号化速度;jemalloc_profile_text_collapsed_use_count:折叠格式按“分配次数”而非“字节数”聚合,默认关闭。
配套的 system 表
system.jemalloc_profile_text用于查看符号化后的堆剖析,attachSystemTables.cpp 中注册时给出了使用前提:“Run 'SYSTEM JEMALLOC FLUSH PROFILE' to generate a profile first”——即必须先执行一次 FLUSH 才能查到数据。
2.3 相关配置项与后续演进
jemalloc 行为的总体开关在 Jemalloc.h 中集中声明了配置键名与默认值:
| 配置键 | 默认值 | 说明 |
|---|---|---|
jemalloc_enable_global_profiler | false | 是否启用全局堆剖析器 |
jemalloc_enable_background_threads | true | 是否启用 jemalloc 后台线程 |
jemalloc_max_background_threads_num | 0 | 后台线程数上限,0 表示由 jemalloc 自行决定 |
jemalloc_collect_global_profile_samples_in_trace_log | false | 是否把全局剖析采样写入 trace log |
jemalloc_profiler_sampling_rate | 19 | 剖析采样率(lg_prof_sample) |
需要注意一个版本演进细节:从当前仓库代码看,全局SYSTEM JEMALLOC ENABLE/DISABLE PROFILE已被标记为废弃路径——InterpreterSystemQuery.cpp中这两个分支直接抛出异常,提示“Please use config 'jemalloc_enable_global_profiler' or enable it per query using setting 'jemalloc_enable_profiler'”。也就是说,在 v23.11 时期这四条命令均可用,但在后续主线中,全局 profiler 的开关收敛到了启动配置(或按查询设置)层面,SQL 命令主要负责 PURGE 与 FLUSH。如果你基于 v23.11.5.29-stable 做运维自动化,ENABLE/DISABLE PROFILE 是可用的;若迁移到新版本,应改用配置项方式。
三、Improvement 1 的 Keeper 侧延伸:jmst/jmfp/jmep/jmdp四条 4LW 命令
同一 PR 还给 Keeper(ClickHouse 的 ZooKeeper 兼容协调服务)加了 4 条四字母命令(4LW,Four Letter Word),同样是 jemalloc 诊断能力:
| 命令 | 对应实现 | 功能 |
|---|---|---|
jmst | JemallocDumpStats | dump jemalloc 统计信息 |
jmfp | JemallocFlushProfile | 在 profiler 启用时 flush 堆剖析 |
jmep | JemallocEnableProfile | 在 profiler 启用时开启堆剖析 |
jmdp | JemallocDisableProfile | 关闭堆剖析 |
源码印证如下:
- 四个命令类定义在 FourLetterCommand.h,整体被
#if USE_JEMALLOC包裹——即只有以 jemalloc 构建时这四个命令才编译进二进制,非 jemalloc 构建下执行会报未知命令。 - 命令行客户端的合法命令白名单里也登记了它们,见 KeeperClient.h,因此可以用
clickhouse-keeper-client -q "jmst"这类方式直接对 Keeper 节点发诊断请求。 - CoordinationSettings.cpp 中默认允许的四字母命令列表包含
jmst,jmfp,jmep,jmdp,说明这四条命令默认是放行的(Keeper 对 4LW 命令有 allow-list 机制,防止误用)。
这条增强的意义在于:Keeper 虽然负载轻,但在高吞吐场景(大量会话、watcher、事务)下同样可能受 jemalloc 内存归还策略影响,4LW 命令让 Keeper 与 Server 具备了同一套内存诊断手段。
四、Improvement 2:Iceberg 引擎的iceberg_engine_ignore_schema_evolution
4.1 设置语义
changelog 第二条记录了 Iceberg 表引擎的一个读取策略开关(PR #59133):
- 新设置
iceberg_engine_ignore_schema_evolution,默认关闭(false); - 开启后,读取 Iceberg 表时忽略 schema 演进:所有数据文件都使用同一份 schema 读取——要么是用户建表时指定的 schema,要么是建表时从元数据解析到的最新 schema;
- changelog 明确警告:对存在 schema 演进的数据集,开启该设置可能得到错误结果,因为所有数据文件都会用同一份 schema 去解释。
4.2 适用场景与源码印证
这个设置的价值在于“一致性优先于正确性”的特定场景:当上游 Iceberg 表 schema 演进历史复杂、或者用户只想按自己定义的视图读全量数据时,逐文件按历史 schema 解析可能不符合预期,此时可以强制用单一 schema 读全部数据。作为交换,你要接受“schema 不匹配的数据可能被误读或丢失”的风险。
在仓库中可以找到该设置的完整生命周期记录:
- 引入记录见 SettingsChangesHistory.cpp:
{"iceberg_engine_ignore_schema_evolution", false, false, "Allow to ignore schema evolution in Iceberg table engine"},登记在 v23.11 版本段,与本次 backport 一致; - 值得注意,在当前主线代码 Settings.cpp 中,该设置已被
MAKE_OBSOLETE标记(默认值仍为false)。这意味着在后续版本里,Iceberg 引擎的读取/schema 演进处理策略发生了重构,v23.11 时期的这一独立开关不再是当前形态。如果你的集群长期停留在 23.11 分支并依赖此设置,升级主线前需要确认新版本的等价行为。
五、Bug Fix:七个用户可见修复逐条解读
该版本的 Bug Fix 部分修复了 7 个“official stable release 中用户可见的错误行为”,以下逐条说明其问题域与影响:
| 修复内容 | 对应 PR | 问题域解读 |
|---|---|---|
| Fix a stupid case of intersecting parts | #58482 | MergeTree 分区间合并(intersecting parts)的边界场景。MergeTree 依赖“part 之间主键范围互不重叠”的不变量来路由查询,该修复消除了一个会导致查询命中错误 part 或合并行为异常的路径。作者 Alexander Tokmakov 是 MergeTree 核心维护者之一 |
| Fix stream partitioning in parallel window functions | #58739 | 并行窗口函数(window = 1下多流并行执行)的流划分(stream partitioning)错误。分区键不一致会导致窗口函数(如row_number、lag等)在错误的数据分组上计算,结果集内部顺序/值出错 |
| Fix double destroy call on exception throw in addBatchLookupTable8 | #58745 | 聚合函数基于查找表(lookup table)的批量计算路径在抛异常时发生“双重释放”(double destroy),属于内存安全问题。addBatchLookupTable8接口定义于 IAggregateFunction.h(约 L400 处声明的虚函数addBatchLookupTable8),并被 Aggregator.cpp 的批处理路径调用;此修复消除了该异常路径上的重复析构 |
| Fix JSONExtract function for LowCardinality(Nullable) columns | #58808 | JSONExtract系列函数处理LowCardinality(Nullable(...))组合列时出错(取不到值或取错值)。这是 JSON 半结构化查询的常见列类型,修复后对这类列的提取结果恢复正确 |
| Fix LIMIT BY and LIMIT in distributed query | #59153 | 分布式查询(LIMIT BY与LIMIT同时存在)下子查询改写/下推错误,可能导致结果行数或分组不符合预期。分布式场景下LIMIT BY需要在各分片与协调端之间正确传递,属于经典的分布式改写类 Bug |
| Fix not-ready set for system.tables | #59351 | system.tables系统表的 not-ready 集合维护错误,可能把尚未就绪的表错误地标记为就绪(或反之),影响依赖系统表做轮询/就绪判断的客户端 |
| Fix translate() with FixedString input | #59356 | 字符串函数translate()处理FixedString输入时结果错误,修复后对定长字符串列的字符映射替换行为正确 |
从修复分布可以看出这个修订版的质量取向:既覆盖存储层(part 边界)、执行层(并行窗口函数、聚合异常路径)、SQL 语义层(JSONExtract、translate),也覆盖系统表与分布式查询改写,适合作为 23.11 分支用户的一次“攒够一批修复”的升级点。
六、NOT FOR CHANGELOG:不计入用户变更的内部修复
changelog 末尾的 “NOT FOR CHANGELOG / INSIGNIFICANT” 部分列出了一些不进正式变更说明的内部改动,其中两条对稳定性敏感的系统仍有参考价值:
Fix rare race in external sort/aggregation with temporary data in cache(#58013):当外部排序/聚合的临时数据落入 cache 时存在一个罕见的竞态条件。开启临时数据缓存(如使用 cache disk 存放 spill 数据)的部署应关注此修复;Fix possible race in ManyAggregatedData dtor(#58624):多行聚合数据结构析构时的潜在竞态,属于并发安全加固。
其余条目为日志级别调整(#59168)、错误信息措辞细化(#57991)以及 #58482 的跟进补丁(#58574)。
七、小结:这个稳定版值得你关注什么
v23.11.5.29-stable 虽然只是一个稳定分支修订版,但技术含量集中在三条主线上:
- 内存可观测性与可控性:
SYSTEM JEMALLOC PURGE让“jemalloc 不还内存”这类问题第一次可以在不重启的情况下主动干预;ENABLE/DISABLE/FLUSH PROFILE加system.jemalloc_profile_text表与格式设置(jemalloc_profile_text_output_format等)构成了完整的堆剖析工作流;Keeper 侧用jmst/jmfp/jmep/jmdp四条 4LW 命令补齐了对称能力(实现见 FourLetterCommand.h)。注意这些能力均要求 jemalloc 构建(源码中大量#if USE_JEMALLOC条件编译)。 - Iceberg 读取策略可配置:
iceberg_engine_ignore_schema_evolution(默认关闭)提供了“按单一 schema 读全量数据”的逃生舱,changelog 同时明确提示了开启后结果可能不正确的风险;该设置在当前主线已被标记 obsolete,说明后续版本对该策略有更完整的重构。 - 执行与存储稳健性:7 个用户可见 Bug 修复覆盖了 intersecting parts、并行窗口函数分区、聚合异常路径双重释放、JSONExtract/translate 函数行为、分布式
LIMIT BY、system.tables就绪状态等高频使用面。
如果你正在使用 23.11 分支,这一版对“内存运维”和“正确性修复”都有实质收益;同时建议在执行SYSTEM JEMALLOC系列命令前确认目标构建确实启用了 jemalloc,并保留SYSTEM级别的访问权限管理,避免普通查询账号误操作全局内存行为。
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考