ClickHouse v21.7.10.4-stable 补丁版本解析:核心 Bug 修复与底层机制全解读
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
导读
v21.7.10.4-stable 是 ClickHouse 21.7 系列的一个补丁维护版本(patch release),相对 v21.7.9.7-stable 仅包含 1 项改进、7 项用户可见 Bug 修复及若干内部变更。本文以官方发布说明为主体,结合当前仓库源码,逐一剖析每项修复涉及的底层机制——包括查询优化器、副本元数据一致性、ZooKeeper watch 管理、子列读取与高阶数组函数等,帮助读者理解这些修复的价值、触发场景以及升级时的注意事项。
版本定位:21.7 系列的滚动维护
docs/changelogs/archive/v21.7.10.4-stable.md是 21.7 系列的归档 changelog 之一。21.7 是 ClickHouse 引入"滚动升级兼容层"的关键版本(见下文legacy_column_name_of_tuple_literal设置),而 v21.7.10.4 作为该系列的后期补丁版本,主要目标不是新增功能,而是:
- 修复影响用户可见行为的缺陷(标记为Bug Fix (user-visible misbehaviour in official stable release)的部分均已在官方稳定版中暴露过);
- 将修复以 backport 方式合入维护分支;
- 兼顾内部稳定性改进。
发布说明中每一项修复都标注了对应的 backport issue 与原始 PR 编号,例如垂直合并改进对应 #28898,这正是 ClickHouse 发布流程中"修复先合入主分支、再 backport 到维护分支"的体现。
改进项:垂直合并使用真实临时文件
发布说明中的唯一一项 Improvement:
Use real tmp file instead of predefined "rows_sources" for vertical merges. This avoids generating garbage directories in tmp disks.
垂直合并(vertical merge)是 ClickHouse 按列进行后台数据合并的机制,合并过程中需要记录"每一行数据来自哪个原始 part"的元信息,此前这一信息被写入名为rows_sources的预定义文件中。如果该预定义路径与临时磁盘(tmp disk)的实际布局不一致,就会在临时磁盘上遗留无用的垃圾目录。
该改进将固定文件名替换为真实创建的临时文件,从而消除这类残留目录。从发布说明的措辞看,它属于内部资源管理层面的健壮性改进,不改变合并的对外语义,但对长期运行、频繁发生合并的服务器而言,能减少临时磁盘上的碎片堆积。
查询优化与语法解析层的修复
PostgreSQL 风格 cast(::运算符)与负数
Fix PostgreSQL-style cast (
::operator) with negative numbers.
ClickHouse 兼容 PostgreSQL 的::类型转换语法(例如SELECT -1::Int32)。该修复针对的是解析阶段负数与::运算符组合时产生的歧义或错误行为。此修复位于语法解析层,直接影响所有使用 PostgreSQL 风格类型转换的用户,属于兼容性体验修复。
OR 链转 IN 优化在分布式查询中的缺陷
Fix transformation of disjunctions chain to
IN... in distributed queries with settingslegacy_column_name_of_tuple_literal = 0.
这是本版本中最值得展开的一项修复,它同时牵涉两个查询级设置,且都有明确的源码依据。
1.optimize_min_equality_disjunction_chain_length:OR 等值链转 IN 的阈值
ClickHouse 的查询优化器会把形如expr = x1 OR expr = x2 OR ... OR expr = xN的析取等值链合并成expr IN (x1, ..., xN),以提升索引与执行效率。该优化由optimize_min_equality_disjunction_chain_length控制,其定义与默认值位于 src/Core/Settings.cpp:
The minimum length of the expression `expr = x1 OR ... expr = xN` for optimization默认值为3,即等值链长度达到 3 才触发合并;设为 0 则完全禁用该优化。
实现该逻辑的类LogicalExpressionsOptimizer位于 src/Interpreters/LogicalExpressionsOptimizer.cpp,其工作流程为:
collectDisjunctiveEqualityChains():遍历 AST,收集满足expr = literal形式、且or表达式无别名(alias)的等值链;mayOptimizeDisjunctiveEqualityChain():校验链长度是否达到阈值、右侧字面量类型是否一致;对LowCardinality列有特殊处理(字典通常较小、索引相对较大,合并为 IN 通常更优),见 LogicalExpressionsOptimizer.cpp;addInExpression():构造expr IN (x1, ..., xN),并对字面量元组排序以保证确定性,见 LogicalExpressionsOptimizer.cpp;cleanupOrExpressions()与fixBrokenOrExpressions():删除已被替换的等值项,并在 OR 只剩单个操作数时将其提升替换原 OR 节点(同时处理 WHERE / PREWHERE / HAVING 根节点),见 LogicalExpressionsOptimizer.cpp。
该优化器在 src/Interpreters/TreeRewriter.cpp 中被调用,属于查询重写(tree rewriter)阶段。
2.legacy_column_name_of_tuple_literal:21.7 滚动升级兼容开关
第二个相关设置是legacy_column_name_of_tuple_literal,定义见 src/Core/Settings.cpp:
List all names of element of large tuple literals in their column names instead of hash. This settings exists only for compatibility reasons. It makes sense to set to 'true', while doing rolling update of cluster from version lower than 21.7 to higher.该设置默认值为false。21.7 版本改变了大型元组字面量列名的生成方式(由"列出所有元素名"改为"使用哈希"),这会影响分布式查询中子查询结果的命名。在做集群滚动升级(从低于 21.7 的版本升到更高版本)时,应临时将其设为true以保证新旧节点之间查询结果列名一致。
本次修复正是针对legacy_column_name_of_tuple_literal = 0(即新行为)与分布式查询场景组合时,OR 链转 IN 优化转换错误的问题。对 21.7 系列用户而言,这项修复保证在新列名行为开启后,分布式查询依然能正确执行该优化。
副本元数据与一致性相关修复
ZooKeeper watches 泄漏
Fixed possible ZooKeeper watches leak on background processing of distributed DDL queue.
分布式 DDL(如ON CLUSTER语句)通过 ZooKeeper 队列在集群内传播,后台处理队列时若 watch 注册/注销管理不当,会累积泄漏 watch,长期运行可能耗尽 ZooKeeper 会话资源或影响处理效率。该修复解决了分布式 DDL 队列后台处理路径上的 watch 泄漏问题。
ReplicatedVersionedCollapsingMergeTree排序键元数据错误
Fix bug which can lead to error
Existing table metadata in ZooKeeper differs in sorting key expression.after alter ofReplicatedVersionedCollapsingMergeTree.
ReplicatedVersionedCollapsingMergeTree是带版本号的多版本折叠合并树引擎。此前对该类表执行ALTER(如修改排序键相关定义)后,ZooKeeper 中保存的表元数据与实际排序键表达式可能不一致,从而在后续操作时报出元数据不一致错误。该修复保证了 alter 后副本间元数据的一致性,避免复制队列被卡住。
ReplicatedMergeTreeQueue良性竞态条件
Fix benign race condition in ReplicatedMergeTreeQueue. Shouldn't be visible for user, but can lead to subtle bugs.
复制表后台依赖ReplicatedMergeTreeQueue调度合并与拉取任务。该修复消除了一处"良性"竞态——虽然通常不会直接暴露给用户,但可能引发难以追踪的细微问题(如偶发的队列状态不一致)。这是典型的防御性修复,对长期运行的复制表集群具有稳定性价值。
子列读取相关修复
There is no subcolumn错误
Fix
There is no subcolumnerror, while select from tables, which haveNestedcolumns and scalar columns with dot in name and the same prefix asNested(e.g.n.id UInt32, n.arr1 Array(UInt64), n.arr2 Array(UInt64)).
当表中同时存在Nested列与"名称含点、且与 Nested 前缀相同"的标量列时(如示例中的n.id是普通列,而n.arr1、n.arr2构成 Nested 列),子列(subcolumn)解析会将二者混淆,导致查询时报There is no subcolumn。该修复正确区分了"同前缀点分名标量列"与真正的 Nested 子列,属于列名解析层的边界情况修复。
发布说明中还有一条相关内部修复(见下文 NOT FOR CHANGELOG 部分):"Fix usage of nested columns with non-array columns with the same prefix [2]",说明该方向共经历了两轮修复。
从 compact parts 读取子列
Fix reading of subcolumns from compact parts.
compact parts(min_bytes_for_compact_part/min_rows_for_compact_part触发的小型合并产物)将多个列打包存储。该修复解决了从 compact parts 中读取子列(如数组的size0、null掩码、Nested展开列)时的错误,确保子列在紧凑存储布局下同样可正确读取。
分布式表创建与高阶数组函数
分布式表参数错误导致 coredump
Fix the coredump in the creation of distributed tables, when the parameters passed in are wrong.
CREATE TABLE ... ENGINE = Distributed(...)在传入非法参数(如错误格式的集群名或分片键)时可能触发进程崩溃(coredump)。该修复将此类错误收敛为正常的异常报错路径,避免进程级崩溃。对需要动态建表的运维场景,这一修复显著降低了误操作导致服务中断的风险。
高阶数组函数与常量的崩溃
Fix higher-order array functions (
SIGSEGVforarrayCompact/ILLEGAL_COLUMNforarrayDifference/arrayCumSumNonNegative) with consts.
高阶数组函数(arrayCompact、arrayDifference、arrayCumSumNonNegative)在处理常量参数(consts)时,分别存在SIGSEGV(段错误)与ILLEGAL_COLUMN(非法列类型)两类异常。这些函数内部通常依赖ColumnArray::getOffsets()与参数列的按行展开,常量列与数据列的内存布局不同,若实现未正确处理 const 包装(如ColumnConst)就会触发上述错误。该修复统一补齐了这些函数对常量输入的处理。
NOT FOR CHANGELOG:内部稳定性变更
发布说明末尾的NOT FOR CHANGELOG / INSIGNIFICANT部分记录了 5 项不进入对外 changelog 的内部变更,它们在理解版本行为时同样有价值:
- 原生协议中增加更多 LC(LowCardinality)检查:加强 native protocol 对 LowCardinality 类型传输的校验;
- CHJIT 自定义内存管理器:为查询编译缓存(JIT)引入独立的内存管理,见
compiled_expression_cache_size相关配置; dictGet对 null 的默认实现:完善字典函数对空值/缺省值的处理路径;- 嵌套列与非数组列同前缀用法修复 [2]:与前述
There is no subcolumn修复同源的第二轮加固; compiled_expression_cache_size降至 128MB:降低编译表达式缓存的上限,以控制 JIT 编译产物占用的内存,适合内存受限环境。
升级与验证建议
综合本版本修复内容,可以给出如下升级建议:
- 21.6 及以下版本向 21.7 滚动升级时,请按官方建议将
legacy_column_name_of_tuple_literal临时设为true,待集群全部节点升级完成后再改回默认值false,以避免分布式查询列名不一致问题(设置定义见 src/Core/Settings.cpp)。 - 使用
arrayCompact/arrayDifference/arrayCumSumNonNegative且参数可能为常量的业务,建议优先升级本版本以消除潜在段错误风险。 - 大规模使用复制表与分布式 DDL 的集群,建议升级以获取 ZooKeeper watch 泄漏、复制队列竞态与元数据一致性修复,降低长期运行隐患。
- 自动化建表脚本中若存在
Distributed引擎误用,本版本的 coredump 修复可避免此类误操作拖垮进程。
所有修复均已合入 v21.7.10.4-stable 维护分支,可在 docs/changelogs/archive/v21.7.10.4-stable.md 查看完整条目及对应的 backport issue 链接。
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考