ClickHouse v21.6.5.37-stable 变更日志深度解析:14 项关键 Bug 修复的源码级解读
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
导读
本文以 ClickHouse 官方 v21.6.5.37-stable 版本的 变更日志 为骨架,逐条剖析该补丁版本中的 14 项 Bug 修复、1 项功能改进与 2 项内部优化。作为 v21.6.4.26-stable 的后续补丁版本,v21.6.5.37 集中修复了 21.6 主版本引入的回归问题(如LowCardinality参与IN时的类型转换崩溃)、长期存在的正确性隐患(如大整数CAST溢出返回错误结果)以及分布式场景下的异常行为(如Distributed Materialized View连接丢失列)。读完本文,你将掌握这些修复背后的代码机制、对应源码位置,以及如何通过查询设置与 SQL 复现、验证相关行为。
版本背景与修复概览
v21.6.5.37-stable 是 ClickHouse 21.6 稳定分支的第 5 个补丁版本,对比对象为 v21.6.4.26-stable。本次发布共包含 27 条条目,按类别划分如下:
| 类别 | 数量 | 主要内容 |
|---|---|---|
| Improvement(改进) | 1 | 后续 Bug 修复的准备工作(PR #23518) |
| Bug Fix(缺陷修复) | 14 | 分布式、格式、聚合函数、类型系统、复制表、查询计划等核心模块的正确性修复 |
| NOT FOR CHANGELOG(内部改动) | 2 | 表达式编译与 ODBC 驱动适配 |
所有修复均以 backport(回移植)方式合入稳定分支,即先在主分支修复验证、再移植到 21.6 分支,因此每一条都带有对应的 backport 跟踪 issue 与原始 PR 编号。这些改动集中体现了补丁版本"只修 bug、不引功能"的发布策略。
Improvement:为后续修复铺路
本次唯一的 Improvement 条目来自 PR #23518(作者 Nikita Mikhaylov),其描述为"这里将列出我准备在此 PR 中修复的所有 bug"——这实际上是 ClickHouse 提交规范中用于聚合多个待修复问题清单的占位条目,其价值在于把分散的 issue 汇总到单一 PR 中进行统一跟踪和批量修复,属于流程性改进而非用户可见的功能变化。
14 项 Bug Fix 逐条源码解读
1. 修复Distributed Materialized View连接时的 "Missing columns" 异常
修复 PR:#24870(作者 Azat Khuzhin)
当用户通过Distributed表查询底层的Materialized View并执行JOIN时,可能抛出Missing columns异常。该问题的根因在于分布式查询在重组(rewrite)物化视图查询语句时,用于连接的列在部分执行节点上未正确推导,导致列名缺失。修复后,分布式物化视图与JOIN的组合查询能够稳定返回正确结果。
提示:分布式物化视图的列一致性依赖
Distributed表引擎对查询的改写逻辑(见 src/Storages/Distributed 相关实现),若在集群中出现Missing columns,可优先排查视图定义与JOIN两侧表的列是否对齐。
2. 修复 Protobuf 格式拆分嵌套消息的序列化错误
修复 PR:#25000,对应 issue #24647(作者 Vitaly Baranov)
Protobuf输入/输出格式在序列化被拆分(split)的嵌套消息时存在错误:当一条 Protobuf 消息嵌套多层且字段被拆分到不同列时,序列化结果与原始结构不一致。修复后,Protobuf格式对嵌套Message类型的读写保持一致。该修复影响FORMAT Protobuf的input_format_protobuf_*系列设置与对应字段映射逻辑,相关实现位于 src/Formats/FormatFactory.cpp 及 Protobuf 专属格式化器目录。
3. 区分不同表的KILL MUTATION,修复意外报错
修复 PR:#25025(作者 Azat Khuzhin)
此前执行KILL MUTATION时,如果集群中有多张表存在同名 mutation,命令可能错误地取消其他表的 mutation 并抛出Cancelled mutating parts异常。修复后的逻辑在定位 mutation 时严格绑定表标识,避免跨表误伤。这保证了 KILL MUTATION 语法(KILL MUTATION WHERE database = 'xxx' AND table = 'yyy')在多表并发变更场景下的精确性。
4. 修复CSVWithNames插入部分列时的报错
修复 PR:#25169,对应 issue #25129(作者 Nikita Mikhaylov)
使用CSVWithNames格式插入列的子集(非全部列)时,会抛出错误。修复后,CSVWithNames/CSVWithNamesAndTypes等带表头格式支持只提供部分列、按表头名匹配插入,与CSV系列格式的input_format_skip_unknown_fields等设置协同工作。该行为对数据导入工具的兼容性很重要,相关解析逻辑位于 src/Formats/FormatFactory.cpp。
5. 修复安装脚本中的 TOCTOU 竞态
修复 PR:#25277(作者 Alexey Milovidov)
TOCTOU(Time-of-check to time-of-use)是一类经典竞态漏洞:程序先检查文件状态、再基于该检查结果执行操作,而两者之间存在时间窗口可被利用。ClickHouse 官方安装脚本(curl | sh模式)中存在的该问题已被修复,提升了 安装脚本 的安全性。
6. 修复大整数CAST溢出:CAST(1e19 AS UInt128)不再返回 0
修复 PR:#25279(作者 Alexey Milovidov)
这是本次最值得关注的正确性修复之一。在旧版本中,浮点数1e19大于UInt64的最大值18446744073709551615(约 1.84e19)但小于UInt128的上限,CAST(1e19 AS UInt128)却错误地返回了0,同时伴随 UBSan(未定义行为消毒器)报告。根因是大整数转换路径中对浮点中间值到 128 位整数的转换存在溢出处理缺陷。
修复后:
SELECT CAST(1e19 AS UInt128); -- 正确返回 10000000000000000000该修复同时消除了 UBSan 报错,说明问题本质是 C++ 层未定义行为(整数溢出)被正确规避。UInt128/UInt256的实现位于 src/Common/wide_integer.h,浮点转换逻辑位于 src/Common/wide_integer_to_string.cpp,读者可在其中看到大整数的内部表示与溢出边界处理。
7. 修复joinGetOrNull在非空列上的行为
修复 PR:#25288,对应 issue #24261(作者 Amos Bird)
joinGetOrNull与joinGet的区别在于:当 join key 在连接表中不存在时,joinGetOrNull返回NULL,而joinGet直接报错。旧版本中,当连接的取值列本身是非空列(not-nullable)时,joinGetOrNull在 key 缺失场景下没有正确构造Nullable包装,导致行为异常。修复后,无论取值列是否允许 NULL,joinGetOrNull都能正确返回NULL。
从源码看,两个函数共用同一个 OverloadResolver(src/Functions/FunctionJoinGet.cpp),通过effective_or_null布尔标志区分,并在joinGetCheckAndGetReturnType中决定返回类型是否需包一层Nullable。官方文档示例(src/Functions/FunctionJoinGet.cpp内嵌文档):
-- 构造 Join 引擎表 CREATE TABLE id_val(`id` UInt32, `val` UInt32) ENGINE = Join(ALL, LEFT, id); INSERT INTO id_val VALUES (1, 11); -- joinGetOrNull:key 不存在时返回 NULL 而非报错 SELECT joinGetOrNull(id_val, 'val', toUInt32(1)), joinGetOrNull(id_val, 'val', toUInt32(999));注意:
Join引擎表及其joinGet系列函数对 key 的哈希布局敏感,此处示例仅为验证修复后的 NULL 语义,实际使用时请参照 Join 引擎文档 配置。
8. 修复LowCardinality参与IN时的Bad cast崩溃
修复 PR:#25290,对应 issue #25187(作者 Nikolai Kochetov)
这是 21.6 主版本引入的回归问题。当LowCardinality列作为IN子句的左侧参数时,会抛出:
Bad cast from type DB::ColumnLowCardinality to DB::ColumnVector<char8_t>根因在于IN操作符在比较路径中未对LowCardinality列做解包(unwrap)处理,错误地试图将其直接转为ColumnVector<char8_t>。修复后,LowCardinality参数参与IN查询可正常工作,且不丢失低基数优化的性能收益。LowCardinality类型的实现位于 src/Columns/ColumnLowCardinality.h,该崩溃的报错文案"Bad cast from type"来自 src/Common/typeid_cast.cpp 中的类型转换失败路径。
9. 修复min/maxMap在Array/Tuple上的 Logical Error
修复 PR:#25298(作者 Kruglov Pavel)
minMap/maxMap系列函数在参数为Array或Tuple类型时抛出Cannot sum Array/Tuple的 Logical Error。修复后,map 类聚合函数在计算键值对的 min/max 时不再误调用求和逻辑。相关实现位于 src/AggregateFunctions/AggregateFunctionsMinMaxMap.cpp,Map类型数据结构的定义可参考 src/DataTypes/DataTypeMap.h。
10.SummingMergeTree支持SimpleAggregateFunction(LowCardinality)
修复 PR:#25300,对应 issue #25134(作者 Nikolai Kochetov)
此前在SummingMergeTree表中声明SimpleAggregateFunction(...)包裹的LowCardinality列会报错。修复后,SummingMergeTree正确支持该组合:
CREATE TABLE t ( key UInt64, value SimpleAggregateFunction(sum, LowCardinality(UInt64)) ) ENGINE = SummingMergeTree ORDER BY key;该修复让SummingMergeTree的预聚合列可以使用LowCardinality压缩存储,兼顾写入聚合与存储优化。SimpleAggregateFunction的解析与存储逻辑位于 src/DataTypes/DataTypeAggregateFunction.h 及 src/AggregateFunctions 目录。
11. 修复 ZooKeeper 断连时ReplicatedMergeTree后台任务阻塞重连
修复 PR:#25306(作者 Alexander Tokmakov)
这是一个可用性关键修复。当ReplicatedMergeTree表失去 ZooKeeper 连接时,后台任务(如 merge、mutation)会等待连接恢复,反而阻塞了表尝试重连的流程,形成互相等待。修复后,ZooKeeper 连接丢失时后台操作会被强制停止,表立即进入重连流程,缩短了复制表的故障恢复时间。相关实现位于 src/Storages/StorageReplicatedMergeTree.cpp,其 ZooKeeper 会话管理依赖 src/Common/ZooKeeper 目录下的客户端实现。
12. 修复quantileDeterministic等函数的非确定性行为
修复 PR:#25313,对应 issue #20480(作者 Alexey Milovidov)
quantileDeterministic这类函数要求:相同输入必须产生相同输出(确定性),因此它采用基于哈希的确定性水库抽样(ReservoirSampler),而非依赖随机数。旧版本中其行为可能出现非确定结果。修复后,quantileDeterministic及同类函数保证了跨节点、跨运行的确定性。
从源码结构看,NameQuantileDeterministic与底层确定性采样器位于 src/AggregateFunctions/AggregateFunctionQuantile.h,其有状态实现为 src/AggregateFunctions/ReservoirSamplerDeterministic.h。值得注意的是,src/Core/ProtocolDefines.h 中专门定义了"序列化quantileDeterministic状态的 skip degree",说明该修复还涉及状态合并时稀疏化(thinning)的一致性——这正是非确定性问题的来源之一。
13. 修复query_plan_filter_push_down优化丢失WHERE条件
修复 PR:#25370,对应 issue #25368(作者 Nikolai Kochetov)
这是查询优化器层面的关键正确性修复。新查询计划优化器(Query Plan)中的"过滤条件下推"优化在特定查询形状下会丢失WHERE条件,导致返回错误结果集。该优化由设置query_plan_filter_push_down控制,默认开启(1)。
从源码看,该设置定义于 src/Core/Settings.cpp:
query_plan_filter_push_down = 1(默认开启)- 0 - 禁用
- 1 - 启用
官方注释明确:该优化将过滤器沿执行计划向下移动,且仅在query_plan_enable_optimizations = 1时生效,属于专家级调试设置。其核心实现在 src/Processors/QueryPlan/Optimizations/filterPushDown.cpp,该文件包含约 1430 行逻辑,针对FilterStep、JoinStep、AggregatingStep、WindowStep等各步骤实现安全的下推规则(含分组集合、标识符归属检查等边界处理)。
如果你在生产环境中遇到"加了WHERE条件但结果没过滤"的可疑行为,可以临时执行:
SET query_plan_filter_push_down = 0; SET query_plan_enable_optimizations = 0;复跑查询对比结果以确认是否触发该优化缺陷,同时向官方提交 issue 附带EXPLAIN PLAN输出。
14. 修复 DDL 中REPLACE列变换器的引号处理
修复 PR:#25391,对应 issue #23925(作者 Amos Bird)
REPLACE列变换器(column transformer)在用于 DDL(如CREATE TABLE ... AS SELECT ... REPLACE(...))时,格式化后的查询语句未正确加引号,导致含特殊字符的表达式执行失败。修复后,DDL 语句生成会对变换器内容做正确的引号转义。列变换器的解析与格式化实现位于 src/Parsers/ASTColumnsMatcher.cpp 与 src/Parsers/ASTColumnsTransformers.cpp,REPLACE变换器的完整语义见 SELECT 语句文档 中的COLUMNS变换器章节。
内部改动(NOT FOR CHANGELOG)
本版本还包含两条不面向用户的内部改动:
- ExpressionActions 只编译必要部分(PR #24273,作者 Maksim Kita):优化表达式动作(
ExpressionActions)的编译范围,避免为不参与计算的动作生成多余代码,属于查询执行路径的性能打磨。 - ODBC 修复(PR #25045 中的 ODBC 相关 helper)的兼容性问题,保证 ODBC 表函数 正常工作。
升级建议与验证清单
v21.6.5.37-stable 没有引入任何破坏性变更,属于纯修复型补丁,建议所有 21.6 用户升级。升级后建议按以下清单回归验证:
- 类型与聚合:执行
CAST(1e19 AS UInt128)应返回10000000000000000000;用LowCardinality列执行IN查询不再崩溃;minMap/maxMap传入Array/Tuple不再报 Logical Error;SummingMergeTree可正常声明SimpleAggregateFunction(sum, LowCardinality(UInt64))列。 - Join 与分布式:
joinGetOrNull在 key 缺失且取值列为非空列时返回NULL;Distributed表连接Materialized View不再抛Missing columns。 - 复制与故障恢复:模拟 ZooKeeper 断连,
ReplicatedMergeTree应快速进入重连而非被后台任务阻塞;多表并发KILL MUTATION不再误取消。 - 格式与 DDL:
CSVWithNames部分列插入、Protobuf 拆分嵌套消息序列化、DDL 中REPLACE列变换器均回归正常。 - 查询优化器:默认开启的
query_plan_filter_push_down优化不再丢失WHERE条件;如对结果存疑,可用SET query_plan_filter_push_down = 0对比验证。
完整变更条目清单见仓库 docs/changelogs/archive/v21.6.5.37-stable.md,更早版本的回溯可查阅 docs/changelogs 目录下的归档文件。
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考