ClickHouse v25.12.2.54-stable 版本全解读:JSON 共享数据序列化升级、文本索引与查询引擎缺陷修复
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
本文以 ClickHouse 官方发布说明 docs/changelogs/v25.12.2.54-stable.md 为骨架,系统梳理该 stable 版本相对 v25.12.1.649-stable 引入的向后不兼容变更、性能优化、功能改进与数十项缺陷修复,并结合当前仓库源码验证底层实现。读者读完后可掌握该版本升级前的兼容性评估要点、JSON/Dynamic 序列化版本机制、system 系统表新列的使用方法,以及文本索引、数据跳过索引、分布式查询等模块已知问题的修复边界。
版本概览:v25.12.2.54-stable 发布了什么
本版本是 ClickHouse 25.12 稳定分支上的一个补丁级发布,属于自动生成的发布说明文档(标题中保留有FIXME占位标记,表明其由 changelog 流水线生成)。相对基线版本 v25.12.1.649-stable,本次发布共包含:
- 2 项向后不兼容变更(Backward Incompatible Change):JSON 高级共享数据序列化默认开启、回滚 simple ALIAS 列的 INSERT 支持;
- 1 项性能改进(Performance Improvement):
distinctJSONPaths聚合函数改为仅读取 JSON 路径元数据; - 5 项功能/体验改进(Improvement):覆盖
system.data_skipping_indices新列、SYSTEM INSTRUMENT交互、几何函数、云存储复制回退与后台日志限流; - 30+ 项缺陷修复(Bug Fix):覆盖 JSON 序列化、文本索引、查询优化器、分布式连接、存储引擎与访问控制等核心模块。
下面按变更类别逐一展开。
升级必读:两项向后不兼容变更
1. JSON 高级共享数据序列化默认启用,降级路径受限
本版本最关键的变更:JSON 列的高级共享数据序列化(advanced shared data serialization)默认启用。该变更由 [PR #92511] 合入并回退移植(Backported)到本版本。
这带来的直接后果是:升级到本版本后,无法再降级到 25.8 以下的旧版本——旧版本无法读取包含 JSON 列的新数据部分(data parts)。
安全升级的官方建议有二(发布说明原文明确给出):
- 将
compatibility设置指向前一版本,整体对齐旧版行为;或 - 在 MergeTree 表上显式设置
dynamic_serialization_version='v2', object_serialization_version='v2'。
这两个序列化版本设置在当前仓库源码中都有明确定义。在 MergeTreeSettings.cpp 中:
object_serialization_version(JSON 数据类型序列化版本,默认"v3")允许取值为v1/v2/v3,且文档注释明确指出只有v3支持变更共享数据(shared data)的序列化版本,这正是"高级共享数据"能力与版本强绑定的原因;dynamic_serialization_version(Dynamic 数据类型序列化版本,默认"v3")允许取值为v1/v2/v3。
与"共享数据"配套的底层参数同样值得关注:object_shared_data_serialization_version默认"advanced",可选map(共享数据存为单个Map(String, String))、map_with_buckets(按桶拆分为多个 Map,提升单路径读取)、advanced(专门的共享数据序列化,大幅提升单路径读取性能,代价是磁盘占用增加)、advanced_chunked(advanced的增强版,按object_shared_data_target_chunk_rows(默认 8192 行)分块以降低含大量唯一路径的 JSON 列在 merge 时的峰值内存)。零层 part(insert 直接产生的 part)默认使用map_with_buckets,以避免高级序列化显著拖慢插入。桶数量由object_shared_data_buckets_for_compact_part(默认 8)与object_shared_data_buckets_for_wide_part(默认 32)控制,上限均为 256。
从实现看,版本选择并非直接读表设置。在 MergeTreeIOSettings.cpp 中,若全局设置merge_tree_use_v1_object_and_dynamic_serialization开启,则强制回落到 V1,否则才采用存储级设置的版本。也就是说,这是一套"全局兜底 + 表级覆盖"的双层版本选择机制,运维人员可通过全局开关一次性规避旧版本兼容问题,也可按表精确控制。
2. 回滚 "Allow INSERT into simple ALIAS columns"
本版本同时回滚了此前合入的 "允许向 simple ALIAS 列执行 INSERT" 功能(原实现见 [PR #84154],本次回滚见 [PR #92849])。官方给出的理由是:该功能与自定义输入格式(custom formats)不兼容,且未受任何设置项保护(not guarded with a setting)。因此本版本重新禁止向 simple ALIAS 列写入,行为回到 25.8 时代,避免了用户在不自知的情况下依赖一个无开关兜底的实验性行为。
性能改进:distinctJSONPaths 不再读取整个 JSON 列
本次唯一的性能优化针对distinctJSONPaths聚合函数([PR #92196]):它现在只从数据部分读取 JSON 路径(paths),而不再读取整个 JSON 列,从而大幅减少 IO 与反序列化开销。
结合源码 AggregateFunctionDistinctJSONPaths.cpp,可以看清该函数的数据流:状态内部持有一个带内存追踪的UnorderedSetWithMemoryTracking<String>用于去重收集路径;add方法分别遍历三类来源——column.getTypedPaths()(强类型路径)、column.getDynamicPathsPtrs()(Dynamic 路径,且跳过当前行为 NULL 的路径)、以及getSharedDataPathsAndValues()+getSharedDataOffsets()定位到的共享数据路径区间(源码 L43-L58)。也就是说,聚合过程只消费"路径"这种轻量元数据,天然适合只读路径文件而非整列数据。该优化正是利用了这一特性:对于 JSON 列,路径元数据存储独立于值数据,聚合时只需扫描路径文件即可返回完整去重路径集合,避免了全列读取。
功能与体验改进(Improvement)
system.data_skipping_indices 新增 creation 列
system.data_skipping_indices系统表新增creation列([PR #92378]),用于标注数据跳过索引的创建方式,取值是一个Enum8:Explicit(0,显式由CREATE TABLE/ALTER TABLE ADD INDEX创建)与Implicit(1,隐式创建,例如经由add_minmax_index_for_numeric_columns这类自动化机制)。
该列的定义可以直接在系统表实现 StorageSystemDataSkippingIndices.cpp 中验证:creation_datatype被构造为带注释 "Whether the index was created implicitly (via add_minmax_index_for_numeric_columns or similar)" 的DataTypeEnum8,与database、table、name、type、type_full、expr、granularity、data_compressed_bytes、data_uncompressed_bytes、marks_bytes等既有列并列。此列可帮助 DBA 快速识别由自动化机制悄悄生成、而非人工显式声明的索引,便于索引治理与清理。
SYSTEM INSTRUMENT ADD/REMOVE 交互重构
SYSTEM INSTRUMENT系列指令用于动态添加/移除函数级别的插桩(instrumentation)。本版本([PR #93345])从三方面改善其 UX:
- 函数名改用字符串字面量(String literals),避免歧义与转义问题;
- 一次指令可 patch 所有匹配的函数(all functions that match),批量操作不再需要逐个点名;
REMOVE子句也允许使用function_name,与ADD保持一致的操作语法。
对应权限模型同样在 InterpreterSystemQuery.cpp 中得到确认:INSTRUMENT_ADD/INSTRUMENT_REMOVE分别要求SYSTEM_INSTRUMENT_ADD/SYSTEM_INSTRUMENT_REMOVE权限(源码 L1291-L1299 做权限检查,L3210-L3217 做 required access 收集),说明该指令是受管控的管理操作。
flipCoordinates 支持 Geometry 类型
几何函数flipCoordinates(翻转坐标)此前仅支持点/线/面数组类输入,本版本([PR #93303])扩展为支持Geometry类型。在函数实现 flipCoordinates.cpp 中可以看到专门的处理:useDefaultImplementationForVariantWithCustomName对Geometry返回 false(源码 L39-L42),即不再走通用的 Variant 适配器,因为通用适配器会重建一个裸Variant并丢失自定义类型名;对Geometry走专用路径以保留类型名,其余Variant仍走适配器并保留其variant_throw_on_type_mismatch语义。这解释了为何该变更需要额外处理,而非简单的类型注册。
Azure Blob Storage 原生复制失败自动回退
针对 Azure Blob Storage 的复制路径([PR #92888]):当原生复制(native copy)失败时,自动回退到"读-写"复制(read-write copy)。此前仅对Unauthorized错误回退(典型场景是跨存储账户复制 blob),但实际中还常出现BadRequest,例如 "The specified block list is invalid"。本次把回退条件放宽为所有原生复制失败场景,提升了跨账户/异常块列表场景下的健壮性。
后台任务日志限流
background_schedule_pool_log新增阈值duration_threshold_milliseconds=30([PR #92965]):仅当后台调度任务执行慢于 30ms 时才记录日志,避免慢于平均水平的任务产生过量日志刷屏,属于可观测性体验优化。
缺陷修复全景(Bug Fix)
本节覆盖本版本全部 30+ 项官方标注的 "user-visible misbehavior" 修复,按模块归类展开。
JSON / 对象 / 稀疏序列化
- 列与子列同名冲突([PR #92453]):当某列与其子列(subcolumn)同名时,可能选中错误的序列化器并导致查询失败(关联 issue #90219、#85161)。修复保证子列场景下序列化器解析的唯一性。
- JSON 'skip' 路径类型提示错误([PR #92842]):修复创建 JSON
skip路径(跳过字段路径)时的类型提示(type hint)错误(关联 issue #92731)。 - 稀疏列 mutation 后 FILE_DOESNT_EXIST([PR #92860]):当
ratio_of_defaults_for_sparse_serialization=0.0时,对稀疏序列化列执行 mutation 可能报FILE_DOESNT_EXIST(关联 issue #92633)。 - 稀疏列 mutation 的第二个场景([PR #93016]):当通过
ALTER将ratio_of_defaults_for_sparse_serialization改为1.0后执行 mutation,同样可能出现FILE_DOESNT_EXIST,一并修复。 - glob 场景 schema 推断缓存错乱([PR #92006]):
file/url等表函数配合 glob 通配符做 schema 推断时,此前会把推断出的 schema 缓存应用到所有匹配文件;现在只缓存推断来源的那个文件,避免不同文件 schema 不一致导致读取失败(关联 issue #91745)。 - 旧版 parquet reader 的 schema 推断([PR #92867]):默认不启用的旧 parquet reader 中,JSON 列出现在 Tuple 列之后时 schema 推断错误,且空 Tuple 解析失败,本次一并修复。
文本索引(Text Indexes)
文本索引(text index,如ngrambf_v1、tokenbf_v1)在本版本获得密集修复,构成一条清晰的"索引全生命周期"修复主线:
- 超大 part 暂禁索引([PR #92644]):包含超过 4,294,967,295 行(即 2^32-1)的大 part 上,创建与物化(materialize)文本索引被临时禁用——现有索引实现尚不支持如此大的 part,强行使用会导致错误查询结果。
- ngram 长度上限修复([PR #92672]):移除
ngrambf_v1索引在 ngram 长度(第一个参数)> 8 时抛出异常的缺陷。 - merge 崩溃修复([PR #92925]):修复 MergeTree 表文本索引合并过程中的多处崩溃。
- 复杂表达式索引合并([PR #93073]):修复构建在复杂表达式(如
concat(col1, col2))之上的文本索引在合并时的崩溃。 - 子列索引重建([PR #93326]):修复构建在子列(subcolumns)之上的文本索引在重建(rebuild)时的问题。
- 未压缩大小统计([PR #92832]):修正
system.parts表中文本索引未压缩大小的计算。
查询优化与执行
- runtime filters 与 LEFT ANTI JOIN([PR #91824]):当
LEFT ANTI JOIN带有额外 post-condition 时,运行时过滤器(runtime filters)工作不正确,已修复。 - count_distinct_optimization([PR #92376]):修复
count_distinct_optimization优化 pass 在窗口函数之上及多参数场景下的错误应用。 - 数据跳过逻辑的 NOT 语义([PR #92726] 与 [PR #93017]):当
WHERE中出现not match(...)、not materialize(...)、not CAST(...)时,数据跳过索引逻辑此前会给出错误结果(关联 issue #92492、#88536)。两处修复共同保证了"取反表达式"不被错误地用于跳过数据。 - has() 空数组索引分析([PR #92995]):
has()函数接收空数组参与索引分析时触发逻辑错误(关联 issue #92906),已修复。 - 轻量更新的主索引使用([PR #92838]):轻量更新(lightweight updates / mutations)的
WHERE谓词包含带子查询的IN子句时,主索引(primary index)此前未被正确使用,已修复。 - 投影重建去冗余列([PR #93233]):merge 过程中重建投影(projection)时移除未使用的列,降低内存占用并减少临时 part 数量。
- 相关标量子查询列误删([PR #93273]):存在标量相关子查询(scalar correlated subquery)时,未使用列移除可能误删仅被子查询使用的列,导致
NOT_FOUND_COLUMN_IN_BLOCK错误,已修复。
分布式与网络连接
- insert-select 连接泄漏([PR #92175]):
INSERT ... SELECT中提前释放 request stream,避免关闭 HTTP 连接(release request stream to prevent closing http connection)。 - 断连 Connection 读取崩溃([PR #92807]):从已断开的
Connection读取可能崩溃,已加防护。 - 分布式查询预取消后的连接状态([PR #93029]):分布式查询被预取消(preliminary cancellation)后,连接遗留为 broken 状态,已修复。
存储与 MergeTree 引擎
- 只读磁盘启动清理([PR #92748]):当 MergeTree 表建立在只读磁盘上时,启动阶段不再尝试删除临时目录。
- ALTER TABLE REWRITE PARTS([PR #92754]):修复执行
ALTER TABLE REWRITE PARTS(v2)时 "Cannot add action to empty ExpressionActionsChain" 报错。 - 共享 part 的 TOCTOU 竞态([PR #93022]):修复共享 parts 场景下因 TOCTOU 竞态可能使用过期 part 的问题。
- S3Queue Ordered 模式([PR #92814]):
S3Queue表引擎Ordered模式下 "Failed to set file processing within 100 retries" 由逻辑错误(logical error)降级为警告——该错误在 25.10 之前的 Keeper 会话过期场景下可能出现;25.10+ 在高并发处理Ordered模式下理论上仍可能触发,故保留为警告。 - SHOW CREATE DATABASE 死锁([PR #92541]):修复对 Backup 数据库执行
SHOW CREATE DATABASE时的死锁。
访问控制
- 通配符 GRANT 检查重做([PR #92725]):重做通配符授权(wildcard grants)的访问权限检查逻辑。此前 [PR #90928] 修复了一个关键安全漏洞,但矫枉过正——一些通配符
GRANT语句因不相关的 revoke 而失败。本次重做在保持安全性的同时恢复合法授权的可用性。
其他
- tokens() 空参数崩溃([PR #92586]):
tokens()函数第二个参数为 NULL 时可能崩溃,已修复。 - Iceberg 的 ORDER BY tuple([PR #93225]):修复 Iceberg 表中按 tuple 排序的问题(关联 issue #92977)。
内部变更与测试调整(NOT FOR CHANGELOG)
发布说明末尾还包含一批不面向用户的内部改动,可归为四类:
- 标记与分级调整:task-based 并行副本相关设置被标记为生产级(production tier,[PR #92577]),而自动并行副本(automatic parallel replicas)模式被标记为实验性([PR #92723]),反映该特性仍处于演进期;
- 代码整理:内联
isArchSupported([PR #92403])、修复JSONPretty格式输出多余尾部空白([PR #92421])、文本索引崩溃修复([PR #92694])、vertical merges 与文本索引的又一处修复([PR #93140])、日志级别调整([PR #92810])、rest catalogs 增加日志([PR #92993]); - 测试优化:禁用 03761 测试的随机化([PR #92517])、精简 02126_dist_desc 测试中冗余的
SYSTEM FLUSH LOGS([PR #92535])、03735 测试移出 fast tests([PR #92538])、修复test_distributed_insert_select_to_rmt_where因去重导致的偶发失败([PR #93130])、修复 system instrument 无状态测试([PR #93420])。
升级与验证建议
综合本版本变更,给出现场升级清单:
- 先评估 JSON 列存量:若库中存在
JSON类型列,升级后将无法降级到 25.8 以下。升级窗口期建议准备回退预案,或按官方建议提前为相关表设置compatibility或dynamic_serialization_version='v2', object_serialization_version='v2',将兼容性风险显式控制在可预期范围内;如需验证当前版本实际使用的序列化版本,可查阅 MergeTreeSettings.cpp 中上述设置的默认值与取值范围。 - 关注文本索引类负载:本版本对文本索引做了大量修复,同时也临时禁用了超大 part(> 2^32-1 行)上的索引创建/物化。存在超大分区表的用户应确认查询计划不会依赖被禁用能力,并验证 merge 不再崩溃。
- 验证数据跳过索引正确性:涉及
not match(...)、not materialize(...)、not CAST(...)与has()空数组的查询,升级后应回归测试结果正确性(对应 [PR #92726]、[PR #93017]、[PR #92995] 的修复范围)。 - 巡检 system 系统表新能力:可利用
system.data_skipping_indices新增的creation列(取值Explicit/Implicit,见 StorageSystemDataSkippingIndices.cpp),对隐式创建的数据跳过索引做一次全面盘点。 - 回归分布式与 JOIN 场景:runtime filters + ANTI JOIN post-condition、分布式查询预取消、insert-select 连接释放等修复涉及网络与连接状态机,建议在升级后执行包含分布式
INSERT ... SELECT与 ANTI JOIN 的冒烟测试。
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考