深入解读 ClickHouse v23.10.4.25-stable 回移植版本:变更日志精读与核心修复的源码级验证
2026/9/14 14:37:49 网站建设 项目流程

深入解读 ClickHouse v23.10.4.25-stable 回移植版本:变更日志精读与核心修复的源码级验证

【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse

本篇技术指南围绕 ClickHouse 23.10 分支的回移植稳定版 v23.10.4.25-stable 的官方变更日志展开,逐条解析该版本包含的 6 个用户可见 Bug 修复与 2 项构建/测试改进,并结合仓库源码验证 GCD/FPC 压缩编解码器崩溃修复、Kerberos 初始化段错误修复、备份恢复设置兼容性等关键改动的实际实现。读完后,你将理解 23.10 分支回移植版本的发布结构,并能将每条 changelog 条目定位到对应的源码模块进行核验。

版本定位:23.10 分支上的一个回移植稳定版

该版本的变更日志位于 v23.10.4.25-stable.md,其标题明确声明了版本对照关系:

ClickHouse release v23.10.4.25-stable (330fd687d41) as compared to v23.10.3.5-stable (b2ba7637a41)

这说明 v23.10.4.25-stable 是构建于提交330fd687d41的补丁发布,变更范围通过与上一稳定版 v23.10.3.5-stable(提交b2ba7637a41,其日志见 v23.10.3.5-stable.md)的差异确定。从条目构成看,这个版本没有 Improvement、Performance、New Feature 等类目,仅包含三类内容:

  • Bug Fix(用户可见的正式稳定版错误行为):6 项,是本版本的核心价值;
  • Build/Testing/Packaging Improvement:2 项,均为 CI 基础设施回移植;
  • NOT FOR CHANGELOG / INSIGNIFICANT:3 项,属于不影响用户行为的工程性改动。

在 23.10 分支的完整版本序列中(该分支从 v23.10.1.1976-stable 一路延续到 v23.10.6.60-stable,见 docs/changelogs/archive/ 目录),v23.10.4.25 处于中段位置。对于锁定 23.10 LTS 分支的生产部署,这类“纯 Bug 修复 + 构建改进”的回移植版本通常是最稳妥的升级目标。

Bug Fix 条目全景

以下是变更日志中 Bug Fix 类目下全部 6 个修复条目,均已在仓库源码中验证了落点:

  1. 修复基于表函数(table function)的表上查询 system 表的问题;
  2. 修复备份恢复(restore from backup)在启用flatten_nesteddata_type_default_nullable设置时的错误;
  3. 修复 Kerberos 初始化过程中的段错误(segfault);
  4. 修复 RabbitMQ 的 OpenSSL 动态加载问题;
  5. 修复 GCD 编解码器在数据中存在零值时的崩溃;
  6. 修复 FPC 编解码器的崩溃。

下面按主题对这 6 个修复逐一展开,并给出源码级的证据链。

压缩编解码器修复:GCD 与 FPC 崩溃的根因

这是本版本中最值得深入的两条修复,两者都指向列级压缩 codec 的崩溃路径。

GCD 编解码器:零值导致 GCD 为 0 的崩溃修复

GCD codec 是一个预处理器型 codec:它先求出整列数值的最大公约数,再用“各值除以 GCD 的商”替代原值进行存储——商更小、熵更低,对后续的通用压缩(如 LZ4、ZSTD)更友好。从源码注释看,该设计对“可整除的整数序列”(例如毫秒时间戳、以固定步长递增的 ID)特别有效,见 CompressionCodecGCD.cpp 第 20–23 行的类注释。

v23.10.4.25 对应的 PR #56704 修复的是“数据中存在零值时崩溃”的问题。根因在数学上很直接:当列中存在 0 时,全列 GCD 的计算结果会退化为 0,而 0 不能作为除数。修复后的逻辑体现在当前源码中:GCD 计算循环(第 110–118 行)逐值用boost::integer::gcd归并,随后:

/// GCD compression is pointless if GCD = 1 or GCD = 0 (happens with 0 values in data). /// In these cases only copy the source to dest, i.e. don't compress. if (gcd == 0 || gcd == 1) { memcpy(dest, source, source_size); return; }

(CompressionCodecGCD.cpp 第 123–129 行)

注释明确写道 “GCD = 0 (happens with 0 values in data)”——当 GCD 为 0(数据含零值)或 1(无可整除性)时,GCD 压缩本身没有意义,直接原样拷贝数据而不做压缩,从而规避了除零崩溃。解压缩路径在decompressDataForType中做了对称处理(第 180–189 行),读到gcd_multiplier为 0 或 1 时按未压缩数据还原,保证新旧数据格式互兼容。

此外,从源码还能看到几个与正确性相关的细节,均与这次“崩溃修复”主题一脉相承:

  • 有符号类型的幅度处理:GCD 未定义于负数,代码对负数取“幅度”(magnitude)参与 GCD 计算,符号通过把商取负来保留(第 103–108 行toMagnitude逻辑);
  • 全程使用无符号运算:第 93–97 行的注释解释了为何内部必须用无符号类型——minInt64的幅度(2^63)无法放入有符号类型,取负会产生未定义行为,而无符号类型通过模 2^N 语义天然给出正确结果;
  • libdivide 加速:对 32/64 位整型用libdivide::divider做快速除法(第 131–147 行),16/32 字节的 UInt128/UInt256 则走普通除法路径。

关于适用范围与参数,注册函数registerCodecGCD(第 307–326 行)表明:GCDcodec 不接受任何参数(传参会报ILLEGAL_SYNTAX_FOR_CODEC_TYPE),只可用于Int*UInt*Decimal*Date*DateTime*类型且值大小须为 1/2/4/8/16/32 字节(第 289–303 行getGCDTypeInfo)。

FPC 编解码器:浮点压缩崩溃修复

FPC(Fast Parallel Compressor)codec 实现了 Burtscher 与 Ratanaworabhan 2008 年论文中的双精度浮点压缩算法,类注释明确引用了论文出处,见 CompressionCodecFPC.cpp 第 20–23 行。其getDescription()返回的描述是 “High Throughput Compression of Double-Precision Floating-Point Data”(第 46 行),并通过isFloatingPointTimeSeriesCodec()标记为时序浮点专用 codec。

v23.10.4.25 中由 Alexey Milovidov 贡献的 PR #56795 修复了 FPC codec 的崩溃。结合当前源码可以印证其结构上的关键点:

  • 压缩级别(level)参数FPC(level)中 level 决定预测器表大小为2^level个浮点值(第 53 行注释),默认值为DEFAULT_COMPRESSION_LEVEL = 12,上限为MAX_COMPRESSION_LEVEL = 28(第 33–34 行);level 非法(小于 1 或大于 28)会在建 codec 时抛ILLEGAL_CODEC_PARAMETER(第 129–131 行);
  • 双预测器竞争编码:内部维护 FCM(Float Compressor,基于前值)与 DFCM(Double-Floating Compressor,基于增量表)两个预测器(FcmPredictor第 198–234 行、DfcmPredictor第 156–194 行),每个值用两个预测器各做一次异或压缩,保留前导零字节更多的结果(第 347–360 行compressValue),以字节对为单位打包,头部 1 字节同时编码两个值的预测器选择位与零字节计数;
  • 64 值分块与头部格式:编码以 64 个浮点值为一个 chunk 进行(第 246 行CHUNK_SIZE = 64),整个压缩块前 2 字节头部存储 float_width 与 compression_level(第 470–473 行),解压时先校验头部合法性(第 490–511 行),对非法 level、错误 float_width 一律抛CANNOT_DECOMPRESS而不是产生越界访问;
  • 解压路径的防御性检查decodePair(第 407–443 行)对空输入、零字节计数越界、长度溢出(__builtin_add_overflow)以及长度不足等情形都显式抛异常,避免读越界。

参数使用上,注册函数registerCodecFPC(第 108–149 行)说明 codec 最多接受 2 个参数:第 1 个为压缩级别(无符号整数,1–28),第 2 个为显式指定浮点宽度(只能是 4 或 8),不传参时宽度从列类型推导,且只适用于Float32/Float64(第 95–104 行getFloatByteWidth,非原生浮点类型会抛BAD_ARGUMENTS)。

Kerberos 初始化段错误修复

变更日志条目 “Fix segfault during Kerberos initialization”(PR #56401)对应 Kerberos 认证初始化路径。当前仓库中该逻辑位于 KerberosInit.cpp:KerberosInit类以编程方式实现了kinit的等价流程(第 34–36 行注释),入口函数kerberosInit(第 220–227 行)负责解析 keytab、初始化krb5_context、解析 principal、处理凭证缓存(cache)并获取/续期票据。

从源码结构看,这段修复涉及的几个关键正确性点都已体现在当前实现中:

  • 全模块置于#if USE_KRB5条件编译内(第 8、228 行):未启用 krb5 构建时整个模块不参与编译,依赖的 krb5 库来自 contrib/krb5 子模块,这也是 v23.10 回移植版本需要修复的构建依赖相关问题背景之一;
  • 资源释放的成对性:析构函数(第 196–218 行)逐一释放 context、keytab、principal、unparsed name、缓存等,且先检查k5.ctx非空再释放——段错误类修复通常就落在“初始化中途失败时部分资源为空指针,析构仍去释放/使用”这类路径上,这里以空指针判断做了防护;
  • keytab 文件存在性前置检查(第 72–73 行)与每个krb5_*调用返回码的检查,避免在错误状态下继续执行;
  • 进程级互斥锁kerberosInit使用静态std::mutex(第 222–224 行,注释说明目的是“防止凭证缓存文件损坏”),避免并发 kinit 竞争同一 credential cache。

备份恢复:flatten_nesteddata_type_default_nullable兼容性修复

PR #56306 修复的是从备份恢复表时,若源端会话启用了flatten_nested(将嵌套类型Nested展平为带.name后缀的独立列)或data_type_default_nullable(列定义中未显式写NULL/NOT NULL修饰符的数据类型默认为Nullable)这两个设置时出现的错误。

data_type_default_nullable这个设置在当前源码中的定义与语义说明位于 Settings.cpp:

DECLARE(Bool, data_type_default_nullable, false, R"( Allows data types without explicit modifiers [NULL or NOT NULL] in column definition will be Nullable. ... )");

从源码结构看,这两个设置都会改变备份中记录的建表语句形态(展平后的列名、默认的 Nullable 修饰),如果恢复端在反序列化元数据、重建表结构时没有按相同设置做等价处理,就会出现列类型不匹配或语句解析失败——这正是该条目要修复的行为。对于使用备份/恢复(BACKUP/RESTORE语句,实现位于 src/Backups 目录)的 23.10 用户,升级到本版本可消除这类“备份侧与恢复侧设置不一致”导致的恢复失败。

其余三条修复与构建改进

system 表与表函数的查询修复(PR #55540):修复了在“基于表函数的表”(如SELECT ... FROM func() AS t这类临时表)上查询 system 表时的问题,属于查询规划阶段的边界情况修正。

RabbitMQ OpenSSL 动态加载修复(PR #56703):针对 RabbitMQ 字典数据源在运行时动态加载 OpenSSL 符号时的加载问题。该数据源依赖contrib下的 AMQP-CPP 库(见 contrib/AMQP-CPP),修复确保了 RabbitMQ 字典在依赖 OpenSSL 的构建配置下可正常初始化。

构建/测试/打包改进共 2 条(均为回移植):

  • PR #56214:修复测试调度计划(setup plan)意外出现在日志中的问题——它本应只出现在runner_get_all_tests.log里,同时确保失败的 infrastructure 事件能发送到 CI 数据库;
  • PR #56689:构建容器(builder container)中不再拉取有变更的 git submodule,缩短构建准备时间。

此外,变更日志的 “NOT FOR CHANGELOG / INSIGNIFICANT” 类目下还有 3 项工程性改动:CI 任务改写为 callable workflow(#56385)、持续将 workflow 改写为可复用测试(#56501)、改进异常消息可读性(#56854)。这些条目不影响用户可见行为,但反映了 23.10 分支维护期 CI 基础设施与主干的持续对齐。

如何核验这类回移植版本

对于维护 23.10 分支的读者,可以按以下方法把 changelog 条目落到可验证的证据上:

  1. 版本对照:先读变更日志标题中的两个 commit 号(330fd687d41vsb2ba7637a41),确认本次发布相对哪个基线;同分支的前后版本日志都在 docs/changelogs/archive/ 中,例如下一版本 v23.10.5.20-stable.md、分支末尾的 v23.10.6.60-stable.md;
  2. 定位修复代码:按条目关键词搜索对应模块。例如 GCD/FPC codec 修复落在 src/Compression/ 下的CompressionCodecGCD.cppCompressionCodecFPC.cpp,Kerberos 修复落在 src/Access/KerberosInit.cpp,设置语义可查 src/Core/Settings.cpp 中对应设置的DECLARE定义与文档字符串;
  3. 关注回移植标记:条目中的 “Backported in #xxx” 表明该修复先合入主干、再 cherry-pick 回 23.10 分支,这类版本的安全性主要来自“只带 Bug 修复、不带行为变更”的构成特征,升级决策时可据此评估风险。

总体而言,v23.10.4.25-stable 是一个典型的补丁式回移植发布:6 个用户可见 Bug 修复覆盖了压缩 codec 崩溃(GCD 零值、FPC)、Kerberos 初始化段错误、备份恢复设置兼容、system 表查询与 RabbitMQ 依赖加载五类问题,配合 2 项 CI 改进,为仍停留在 23.10 分支的部署提供了一个低风险的稳定升级点。

【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询