ClickHouse v23.9.5.29-stable 版本解读GCD/FPC 压缩编解码器崩溃修复与稳定性改进【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse本文基于仓库 docs/changelogs/archive/v23.9.5.29-stable.md 展开聚焦 v23.9 系列第五个补丁版本的核心变更7 项用户可见的 Bug 修复、2 项构建/测试/打包改进并结合 src/Compression/CompressionCodecGCD.cpp 与 src/Compression/CompressionCodecFPC.cpp 源码深入剖析本次修复中技术含量最高的 GCD 与 FPC 编解码器崩溃问题。读完本文你将了解该补丁版本修复了哪些真实缺陷、这些缺陷的根因与修复思路以及如何在自己的部署中规避同类问题。一、版本概览v23.9.5.29-stable 修复了什么v23.9.5.29-stable提交 f8554c1a1ff是 v23.9 稳定分支上的第五个补丁版本相较于上一补丁版本 v23.9.4.11-stable74c1f49dd6a仅做了针对性修复不引入新功能符合补丁版本只修不增的发布策略。该版本的变更按 Changelog 分类可归纳为三大块分类数量主要内容Build/Testing/Packaging Improvement2CI 日志收敛、构建容器子模块拉取优化Bug Fix用户可见行为修复7系统表查询、UNION、Object(JSON)、Kerberos、RabbitMQ、GCD codec、FPC codecNOT FOR CHANGELOG / INSIGNIFICANT4CI workflow 重构、异常信息改进等内部事项下面重点展开 7 项用户可见的 Bug Fix 及其背后的实现细节。二、七项用户可见 Bug Fix 逐一解析1. 修复基于表函数创建的表进行系统表查询#55540此前当表本身由表函数table function构建例如通过merge()、file()等表函数时对其执行系统表system tables查询可能得到错误结果。本版本修复了这一场景下的行为保证SELECT ... FROM system.*与表函数创建的表交互时结果正确。2. 修复 view() 函数中 UNION 查询结果不完整#56274当用户在view()函数内使用UNION组合多个查询时可能出现结果集不完整的问题。这是查询管道QueryPipeline在视图场景下对 UNION 分支处理不充分导致的本版本补齐了该路径。3. 修复添加 Object(JSON) 类型列时崩溃#56307Object(JSON)是 ClickHouse 用于存储半结构化 JSON 的动态类型。此前通过ALTER TABLE ... ADD COLUMN添加类型为Object(JSON)的列时在特定场景下会触发崩溃crash。该修复直接影响依赖半结构化数据的用户。4. 修复 Kerberos 初始化时的段错误#56401在启用 Kerberos 认证的环境如对接 HDFS/Kafka 等 Kerberos 服务中初始化阶段存在触发segfault的缺陷。本版本修复了该初始化路径避免服务启动或首次认证时进程异常退出。5. 修复 RabbitMQ OpenSSL 动态加载问题#56703使用 RabbitMQ 表引擎且依赖 OpenSSL 动态加载的场景下此前存在库加载失败的问题本版本予以修复保障消息队列数据管道的连通性。6. 修复 GCD codec 在数据含零时崩溃#56704这是本版本中技术含量最高的修复之一涉及整数压缩预处理器 GCD codec详见第三节源码剖析。7. 修复 FPC codec 崩溃#56795FPC 是专用于浮点时间序列的压缩 codec本版本修复了其崩溃问题详见第四节源码剖析。三、源码剖析GCD codec 在数据含零时的崩溃与处理GCD codec 的核心思想在 src/Compression/CompressionCodecGCD.cpp 的注释中写得很清楚GCD compression finds the greatest common divisor of the columns values and stores each values quotient by it instead: smaller, lower-entropy quotients compress better downstream. GCD is not defined for negative numbers, so it is computed over the valuesmagnitudes; sign is preserved by negating the quotient, not the foundgcditself.即先求出一列整数值的最大公约数GCD再存储每个值除以该公约数的商商更小、熵更低后续交给通用压缩算法如 LZ4、ZSTD时压缩率更高。GCD 对负数无定义因此算法取每个值的**绝对值magnitude**求公约数符号通过商的符号保留。3.1 崩溃根因gcd 0 的边界分支在compressDataForType模板函数中CompressionCodecGCD.cpp算法先遍历数据计算 gcdT gcd 0; const auto * cur_source source; while (gcd ! T(1) cur_source source_end) { T val unalignedLoadT(cur_source); T magnitude toMagnitude(val); gcd (cur_source source) ? magnitude : boost::integer::gcd(gcd, magnitude); cur_source sizeof(T); }当数据中出现0值时boost::integer::gcd(gcd, 0)的结果仍为gcd本身若整列全为0则最终gcd 0。源码在 CompressionCodecGCD.cpp 明确注释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. dont compress.也就是说gcd 为 0 或 1 时压缩无意义应退化为原样拷贝memcpy。本版本修复的正是这条退化路径在数据含零时暴露的崩溃——修复后gcd 0 || gcd 1时直接memcpy(dest, source, source_size)并返回解压端CompressionCodecGCD.cpp也同步处理了gcd_multiplier 0 || gcd_multiplier 1的特殊分支保证压缩/解压两侧对称。3.2 负数的符号处理无符号中间运算的巧妙设计值得关注的是 GCD codec 的一个实现细节内部算术一律使用无符号类型static_assert(!is_signed_vT)即使列本身是有符号类型。注释CompressionCodecGCD.cpp解释了原因the magnitude of the most negative value (2^(N-1)) does not fit in the signed type, and negating it there would be undefined behavior, whileT(0) - valon unsigned T produces it exactly.例如Int64的最小值INT64_MIN其绝对值2^63无法在Int64中表示直接取负是未定义行为而在无符号类型上执行T(0) - val则能精确得到其模 2^N 意义下的值。符号的还原通过判断magnitude 是否等于原值实现CompressionCodecGCD.cppT quotient (is_signed magnitude ! val) ? T(0) - quotient_magnitude : quotient_magnitude;即若原值为负magnitude 与 val 不同商也取负。3.3 性能细节libdivide 加速除法当元素宽度不超过 8 字节时GCD codec 使用 libdivide 将除以 gcd替换为一次乘法与移位避免逐元素执行昂贵的除法指令仅当宽度为 16/32 字节如Int128、Decimal128等时才回退到普通除法。3.4 适用约束与使用建议注册逻辑位于 CompressionCodecGCD.cpp对适用类型有严格校验仅适用于Int*、UInt*、Decimal*、Date* 或 DateTime*类型元素宽度必须是 1、2、4、8、16、32 字节之一GCD不接受任何参数0 参数否则抛ILLEGAL_SYNTAX_FOR_CODEC_TYPE。-- 用法示例建表时指定压缩列 CREATE TABLE t ( id UInt64 CODEC(GCD, LZ4), dt Date CODEC(GCD) ) ENGINE MergeTree ORDER BY id;GCD是一个预处理器 codec源码中isCompression()返回false本身不产生最终压缩而是把数据规约后再交由后续 codec 压缩。它对可被公约数整除的整数序列如计数器、时间戳增量、固定步长采样值效果显著若数据的 gcd 恰好为 1则完全退化为拷贝无收益。四、源码剖析FPC codec 的崩溃修复与浮点压缩原理FPC codec 是对浮点时间序列的专用压缩器实现在 src/Compression/CompressionCodecFPC.cpp 中。源码头部注释CompressionCodecFPC.cpp说明其算法来源An implementation of the FPC codec for floating-point values described in the paper M. Burtscher, P. Ratanaworabhan: FPC: A high-speed compressor for double-precision floating-point data (2008). Note: The paper only describes compression of 64-bit doubles and leaves 32-bit floats to future work. The code implements them anyways.即算法源自 2008 年 Burtscher 与 Ratanaworabhan 的论文《FPC: A high-speed compressor for double-precision floating-point data》。原论文只覆盖 64 位 double本实现额外支持了 32 位 float。4.1 参数与默认值从 CompressionCodecFPC.cpp 与注册逻辑CompressionCodecFPC.cpp可知第 1 个参数为压缩级别取值范围 1~28默认DEFAULT_COMPRESSION_LEVEL 12预测器表大小为2^level * float_width字节级别越高预测器表越大、压缩率潜力越高第 2 个参数为浮点宽度仅允许 4Float32或 8Float64默认由列类型自动推导getFloatByteWidth见 CompressionCodecFPC.cpp仅接受Float*原生浮点类型参数最多 2 个且必须是无符号整数否则抛出ILLEGAL_SYNTAX_FOR_CODEC_TYPE或ILLEGAL_CODEC_PARAMETER。-- 用法示例显式指定压缩级别 CREATE TABLE sensor_data ( ts DateTime, value Float64 CODEC(FPC(12), ZSTD) ) ENGINE MergeTree ORDER BY ts;4.2 工作原理FCM 与 DFCM 双预测器FPC 的核心是双预测器结构CompressionCodecFPC.cppFCMFinite Context Method预测器基于前一个值预测下一个值DFCMDifferential FCM预测器基于前一个差值预测下一个差值。两个预测器同时工作谁预测得更准就采用谁的残差残差 XOR 原值后的低熵字节流再由后续通用压缩器处理。这正是论文中高速压缩双精度浮点数据的工程实现。编码时按encodePair成对处理浮点数见 CompressionCodecFPC.cpp输出格式含 2 字节头部HEADER_SIZE 2。4.3 与 GCD codec 的定位差异维度GCDFPC适用类型Int/UInt/Decimal/Date/DateTimeFloat32/Float64角色预处理器isCompression false压缩器isCompression true适用数据可被公约数整除的整数序列浮点时间序列参数无参数0~2 个level、float_width本版本修复的 FPC 崩溃问题正是针对浮点时间序列这一高频场景的可靠性补丁修复后Float32/Float64列使用 FPC 不再有崩溃风险。五、Build/Testing/Packaging 改进补丁版本同时包含 CI 基础设施的两处改进#56214将安装/设置计划setup plan日志从主构建日志中移出仅保留在runner_get_all_tests.log中同时把失败的基础设施事件上报到 CI 数据库便于定位 CI 自身故障而非代码故障#56689构建容器builder container不再拉取变更的子模块submodule减少 CI 构建环境的不确定性提升构建可复现性。另有四项不对外部用户可见的内部改动NOT FOR CHANGELOG / INSIGNIFICANTDocker 镜像 enrich 改进#55793、将 jobs 改写为可调用 workflow#56385、继续将 workflow 重构为可复用测试#56501、更优的异常信息#56854以及一个未分类的向后移植条目#56598。这些均属于 ClickHouse 持续进行的 CI 现代化改造对最终用户体验无直接影响。六、升级建议与总结v23.9.5.29-stable 是一个典型的小而稳的补丁版本其价值集中体现在崩溃类修复优先升级如果您的表使用了CODEC(GCD)且数据可能含零值或大量使用Float*列配合CODEC(FPC)强烈建议升级到本版本以消除崩溃风险半结构化与安全链路使用Object(JSON)动态列、Kerberos 认证或 RabbitMQ 表引擎的环境同样受益于 #56307、#56401、#56703 三处修复查询正确性涉及view()UNION或表函数 系统表查询的场景升级后可获得正确结果。结合源码可见GCD 与 FPC 两个 codec 的修复体现了 ClickHouse 压缩层对边界条件零值、数据对齐、类型宽度的严谨处理GCD 在gcd 0/1时退化为拷贝并保持编解码对称FPC 则围绕论文算法给出了带参数的工程化实现。相关实现可继续在 src/Compression/CompressionCodecGCD.cpp 与 src/Compression/CompressionCodecFPC.cpp 中深入研读。【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考