ClickHouse v26.1.3.52-stable 版本更新详解:S3Queue 元数据缓存、Variant/JSON 修复与分布式查询稳定性改进
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
本文基于当前仓库中 docs/changelogs/v26.1.3.52-stable.md 这一官方发布日志,逐条解析 ClickHouse v26.1.3.52-stable 相比 v26.1.2.11-stable 引入的向后不兼容变更、性能改进、功能增强与 30 余项用户可见 Bug 修复。读完本文,你将掌握该版本对S3Queue/AzureQueue系统表命名的影响、Nullable(Tuple)类型推断的新行为、以及 JOIN、JSON、Variant、分区裁剪等高频场景下的修复要点与升级注意事项。
一、版本概览
本次发布的完整信息如下:
- 版本号:
v26.1.3.52-stable - 提交哈希:
5549f2acae9 - 对照基线:
v26.1.2.11-stable(53779390caa) - 条目结构:分为 Backward Incompatible Change(2 条)、Performance Improvement(1 条)、Improvement(4 条)、Bug Fix(30 余条)、Build/Testing/Packaging Improvement(1 条)以及若干内部维护类条目。
值得说明的是,原始发布日志标题中带有FIXME标记,这是 ClickHouse 发布流程中用于提醒维护者核对发布基线的占位符,实际发布内容以本文所解析的条目为准。该版本属于 26.1 系列的 stable 分支,是在前一个 stable 版本基础上回移(backport)了大量修复后的维护性发布,其显著特征是:不引入新特性,而是以稳定性和正确性修复为主。
二、向后不兼容变更:S3Queue / AzureQueue 系统表重命名
本版本最需要升级者注意的变更来自 PR #95809(作者 Kseniia Sumarokova):为限制S3Queue/AzureQueue在内存中的元数据占用,相关系统表被统一重命名,查询这些系统表的存量脚本/监控面板需要同步修改。
| 旧表名 | 新表名 | 说明 |
|---|---|---|
system.s3queue | system.s3queue_metadata_cache | 展示 S3Queue 每个文件处理状态的内存态(正在处理 / 已处理 / 失败),非持久化 |
system.azure_queue | system.azure_queue_metadata_cache | 展示 AzureQueue 的内存态 |
system.s3queue_log | 不变 | 持久化表,记录 processed / failed 文件的历史信息 |
在仓库源码中可以验证这一命名结构。例如 src/Storages/ObjectStorageQueue/registerQueueStorage.cpp 中明确写道:
For introspection use the
system.s3queue_metadata_cacheandsystem.s3_queue_metadatastateless tables and thesystem.s3queue_logpersistent table.
同时该文件还给出了新表的实际 DDL(CREATE TABLE system.s3queue_metadata_cache ...)以及SELECT ... FROM system.s3queue_metadata_cache的示例用法。对于 Azure 队列,registerQueueStorage.cpp 中说明:服务端版本 >= 25.1 时使用system.azure_queue_metadata_cache查看内存态,更早版本则回退到system.s3queue_metadata_cache(其中同样包含 azure 表的信息)。系统表在启动时的挂载注册逻辑位于 src/Storages/System/attachSystemTables.cpp。
升级建议:
- 将依赖
system.s3queue或system.azure_queue的查询语句、Grafana 面板、告警脚本统一替换为新表名; - 内存态表用于排障实时状态,长期统计仍应使用持久化的
system.s3queue_log; - 该重命名的动机是限制 S3/Azure 队列的 in-memory 元数据规模,因此对元数据量大的队列,新表本身也更适合按需查询而非全量扫描。
另一条向后不兼容变更是 PR #95811:修复了Variant类型不匹配时的逻辑错误(logical error)。它改变了此前在类型不匹配场景下可能抛出的异常行为,属于行为修正,一般不影响正常 SQL 语义。
三、性能改进:CachedOnDiskReadBufferFromFile 结构体缩小约 50 倍
PR #96098(作者 Azat Khuzhin)将CachedOnDiskReadBufferFromFile结构体的大小缩减了约 50 倍("Trim size ... 50x")。
该结构体是 ClickHouse 本地磁盘缓存(local_filesystem_read_method为缓存读路径)的核心实现,位于 src/Disks/IO/CachedOnDiskReadBufferFromFile.h 与 src/Disks/IO/CachedOnDiskReadBufferFromFile.cpp。从源码结构看,它被用于ReadPipeline的磁盘缓存阶段(见 src/IO/ReadSettings.h 中 "Used by CachedOnDiskReadBufferFromFile and the ReadPipeline disk cache stage" 的注释),同时也被 S3 对象存储的缓存磁盘(CachedObjectStorage)复用,参见 src/Disks/DiskObjectStorage/ObjectStorages/Cached/CachedObjectStorage.cpp。
影响面:每个读取缓冲区实例占用的内存显著下降。在高并发、大量文件读取的负载下(例如高频小文件查询、多查询并发读取同一 S3 数据集),该改进能降低内存占用并减少缓存未命中带来的分配开销。对普通用户而言是透明的性能收益,无需任何配置变更。
四、功能改进(Improvement)
4.1 Schema inference 支持allow_experimental_nullable_tuple_type
PR #95525(作者 Nihal Z. Miaji)让格式文件的 schema 推断(schema inference)开始尊重allow_experimental_nullable_tuple_type设置。开启后,推断出的元组类型可以是Nullable(Tuple(...)),缺失的嵌套对象会变成NULL,而不是「由若干个NULL元素组成的元组」。这一行为差异对 JSON/JSONEachRow 等半结构化数据入表时的 nullability 语义影响明显。
仓库中该设置的实现位于 src/Core/Settings.cpp,定义形式为DECLARE_WITH_ALIAS(Bool, enable_nullable_tuple_type, true, ...),即当前正式名称为enable_nullable_tuple_type(默认开启),并保留allow_experimental_nullable_tuple_type作为别名(alias)。从 src/Core/SettingsChangesHistory.cpp 中的设置演进记录可以看出该设置的演变路径:
| 变更 | 旧值 | 新值 | 说明 |
|---|---|---|---|
Nullable(Tuple)GA | false | true | 该设置被重命名为正式名称 |
enable_nullable_tuple_type新增 | false | false | 作为allow_experimental_nullable_tuple_type的别名引入 |
allow_experimental_nullable_tuple_type新增 | false | false | 最初为实验性设置 |
从源码结构还可以看到另一个关联设置:文档注释中明确指出,该设置并不控制从 Dynamic、Variant、JSON 或 Tuple 列提取出的子列是否可以为Nullable——那由allow_nullable_tuple_in_extracted_subcolumns控制。也就是说,本版本修复的是「推断(inference)阶段」对 nullable tuple 的尊重问题,与「提取子列阶段」的控制点相互独立。
4.2 依赖升级与对象存储查询优化
- libxml2 升级到 2.15.1(PR #95574):内部携带的 libxml2 库从旧版本升级至 2.15.1,主要影响 XML 格式的解析能力与安全性,对使用
XML输入格式或依赖 XML 解析(如部分集成场景)的用户有益。 - 查询
DeltaLake表时跳过对象存储读取(PR #95899):查询system.tables时不再为 DeltaLake 表发起对象存储读取。此前列出系统表会触碰底层存储元数据,耗时且可能触发不必要的远程请求;修复后元数据列举更快、更省网络开销。 - 更完善的 jemalloc 内省能力(PR #96840):增强对 jemalloc 分配器的 introspection,便于诊断内存占用来源,属于可观测性改进。
五、Bug Fix 详解:覆盖 JOIN、JSON、Variant、分区裁剪等高频场景
本次发布包含 30 余条 Bug Fix,均标注为"user-visible misbehavior in an official stable release"。以下按子系统归类解析,并对其中影响面较大的修复给出仓库级佐证。
5.1 查询计划与 JOIN 相关
- 多 JOIN +
USING的类型推断修复(PR #95157):此前在SELECT t2.a FROM t1 LEFT JOIN t2 USING (a) LEFT JOIN t3 USING (a)这类查询中,后续 JOIN 会错误地把未被该 JOIN 涉及的源列类型统一升级为公共超类型。修复后t2.a的类型只由t1.a与t2.a决定,排除t3.a的影响。此前该问题可能导致逻辑错误甚至崩溃(当函数期望的列类型与执行计划实际类型不一致时)。 - 外连接与多 INNER JOIN 组合的错误重排(PR #96193):当外连接的 ON 条件引用多个先前已连接表的列时,优化器可能未考虑全部表依赖关系而错误重排 JOIN,导致 LEFT/RIGHT/FULL 外连接与多个 INNER JOIN 组合的查询返回缺失行。
directjoin 算法 + 空 MergeTree 表(PR #95935):修复了direct连接算法遇到空 MergeTree 表时的NOT_SUPPORTED错误。- 旧分析器中 JOIN 与重复别名崩溃(PR #96405):修复旧 analyzer 下 JOIN 与重复别名组合导致的崩溃。
5.2 JSON 数据类型
tupleElement应用于 JSON 数组时崩溃(PR #95647):修复了tupleElement作用于 JSON 数组时可能崩溃的问题。tupleElement嵌套路径返回错误结果(PR #95907):修复了 JSON 嵌套路径上执行tupleElement可能返回错误查询结果的问题。JSON(SKIP path)跳过路径修复(PR #95948):此前JSON(SKIP path)会跳过所有带path前缀的键,例如"pathpath"也会被误跳过,导致插入时这些路径数据丢失。修复后仅跳过完全等于"path"的键。注意这是一条会改变插入行为的数据安全类修复,使用JSON(SKIP ...)的用户建议核对存量写入数据。
5.3 Variant / Dynamic / 聚合函数
FunctionVariantAdaptor的 const 参数逻辑错误(PR #97116):修复了需要 const 参数的函数(如arrayROCAUC)作用于 Variant 列时的逻辑错误。FunctionVariantAdaptor的Nothing类型处理(PR #97213):当函数作用于 Variant 列返回Nothing类型时(UNION ALL中的空数组可触发),修复了LOGICAL_ERROR异常。optimize_syntax_fuse_functions与LowCardinality(Nullable)(PR #96239 中有完整定义:开启后(默认true),对具有相同参数的sum、count、avg至少两个聚合函数,会融合为一次sumCount()调用(返回(sum, count)元组),从而只扫描一次数据;该设置被SET optimize_syntax_fuse_functions = 0关闭。其优化实现位于 src/Analyzer/Passes/FuseFunctionsPass.cpp,对应的聚合函数实现是 src/AggregateFunctions/AggregateFunctionSumCount.cpp。indexOfAssumeSorted不兼容类型(PR #96877):修复了indexOfAssumeSorted在类型不兼容(如 IPv4 数组配整型搜索值)时的std::terminate异常。
5.4 LIMIT/OFFSET 与执行控制
- 负的
LIMIT/OFFSET分布式查询逻辑错误(PR #95357):修复分布式查询中负数 LIMIT/OFFSET 触发逻辑错误的场景。 - 分数的
LIMIT/OFFSET分布式查询逻辑错误(PR #96475):继续修复分数 LIMIT/OFFSET 在分布式查询中的逻辑错误。 max_execution_time过大导致的 livelock(PR #96450):修复了高max_execution_time值下 cancellation checker 线程的活锁问题;相关的CancellationChecker线程竞争修复见 PR #95563(内部条目)。- ProcessList 潜在死锁(PR #96182):修复了内存 overcommit tracker 在向 cancellation checker 添加任务时可能触发的锁序反转导致的死锁。
5.5 分区裁剪与索引
not IN/not has分区裁剪错误(PR #96241):修复了某些情况下not IN与not has函数分区裁剪不正确的问题。use_primary_key禁用时的 use-after-free(PR #96112):修复了禁用use_primary_key且条件析取项极多时索引分析中的 use-after-free。- 查询条件缓存哈希冲突(PR #96172):修复了 CTE 折叠常量场景下查询条件缓存哈希冲突导致错误结果的问题。
5.6 物化投影、TTL 与 ALTER
_minmax_count_projection与 TTL(PR #96703):修复了 TTL merge 后某块所有行被过滤时,min(timestamp)通过_minmax_count_projection返回1970-01-01纪元值的问题。- 轻量更新后
DROP COLUMN失败(PR #96861):修复了对同一列先执行 lightweight update(UPDATE ... WHERE的轻量更新)再执行ALTER TABLE DROP COLUMN失败的问题。
5.7 权限与角色
ATTACH视图 SQL Security 权限提升风险(PR #94865):移除ATTACH查询中视图 SQL Security 不必要的权限跳过检查,防止用户以未经验证权限的 definer 附加视图造成权限提升。这是安全相关修复,建议尽快升级。AccessRights::contains部分撤销返回错误(PR #96170):修复部分撤销(partial revokes)场景下权限包含判断结果错误。- 撤销默认角色(PR #96103):修复 revoke 默认角色失败的问题。
5.8 可观测性与格式
- 全局 profiler 周期截断(PR #96048):修复了由
global_profiler_real_time_period_ns与global_profiler_cpu_time_period_ns控制的全局 profiler 周期——此前使用的是截断值而非设定值,导致 profiler 唤醒过于频繁。 system.asynchronous_metric_log的 event_date(PR #95947):修复该日志表 event_date 不正确的问题。formatDateTime未初始化值(PR #96133):修复非定宽格式化器(MySQL 风格、JODA 风格)下的 use-of-uninitialized-value。
5.9 内存安全与稳定性(内部维护类)
- 修复
CREATE TABLE带 constraints 时的heap-use-after-free(PR #96669)。 - 修复向量相似度索引中的
stack-use-after-scope(PR #96259)。 - 修复
MergeTreeReadPoolBase的析构顺序(PR #95393)。 - 修复
applyUncommittedState对无路径 delta 的断言失败(PR #95846)。 - 恢复特殊 MergeTree 允许空 order by keys 的行为(PR #96058)。
- bech32 witness 版本校验,避免缓冲区溢出(PR #96671)。
5.10 数据湖集成(Iceberg / Delta Lake)
- Iceberg position delete 空引用(PR #96061):修复 manifest 中 position delete 引用数据文件为 null 时无法获得正确边界的问题。
- Iceberg ORDER BY 崩溃(PR #96484):修复 iceberg 查询中 order by 的崩溃问题。
- Iceberg position delete 日志增强(PR #95901):增加 position deletes 追踪日志。
- Delta Lake 分区数据 squashing(PR #95773):修复分区化 delta lake 数据合并(squashing)问题。
5.11 窗口函数与其他
- 窗口函数 +
group_by_use_nulls(PR #96878):修复group_by_use_nulls = 1且使用 CUBE/ROLLUP/GROUPING SETS 时窗口函数的Bad cast异常。 arrayJoin重复行(PR #96989):修复部分谓词下推优化错误地将含arrayJoin的过滤条件下推到 JOIN 下方,导致arrayJoin配合 INNER JOIN 与 WHERE 产生重复行的问题。- 运行时过滤中的 Nullable 连接列竞态(PR #95775):修复运行时过滤(runtime filters)中 Nullable 连接列的竞态条件。
六、构建与测试改进
- Docker 镜像版本固定(PR #96500):将第三方 Docker 镜像固定到具体版本,提升构建可复现性,避免基础镜像漂移导致的构建不一致。
- 另有 PR #97289 标记为
NO CL CATEGORY,属于发布流程中的杂项提交。
七、升级建议与验证要点
综合以上分析,从 v26.1.2.11-stable 升级到 v26.1.3.52-stable 时建议重点核对:
- 系统表改名(必须行动):
system.s3queue→system.s3queue_metadata_cache、system.azure_queue→system.azure_queue_metadata_cache,更新所有依赖这些表的脚本与监控;历史统计请使用system.s3queue_log。 - 安全修复优先:
ATTACH视图权限检查(PR #94865)属于权限提升类修复,公网暴露的实例应尽快升级。 JSON(SKIP path)行为修正:若你的表结构使用了JSON(SKIP ...),本版本改变了键匹配语义(精确匹配代替前缀匹配),建议审查相关写入链路。- 数据正确性类修复:多 JOIN +
USING的类型推断、外连接重排、tupleElement嵌套路径、_minmax_count_projection与 TTL、轻量更新后 DROP COLUMN 等修复,建议在升级后的回归测试中覆盖对应查询形态。 - 性能收益无需配置:
CachedOnDiskReadBufferFromFile内存占用缩小、DeltaLake 系统表元数据跳过对象存储读取等改进为透明收益。
相关实现与文档的仓库路径汇总:
- 发布日志原文:docs/changelogs/v26.1.3.52-stable.md
- S3/Azure 队列元数据缓存表注册与使用说明:src/Storages/ObjectStorageQueue/registerQueueStorage.cpp
- 系统表挂载:src/Storages/System/attachSystemTables.cpp
- 磁盘缓存读取缓冲区实现:src/Disks/IO/CachedOnDiskReadBufferFromFile.h
optimize_syntax_fuse_functions与enable_nullable_tuple_type设置定义:src/Core/Settings.cpp- 聚合函数融合 Pass:src/Analyzer/Passes/FuseFunctionsPass.cpp
- 设置演进历史(
Nullable(Tuple)GA 记录):src/Core/SettingsChangesHistory.cpp
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考