news 2026/9/15 11:19:17

ClickHouse v23.9.3.12-stable 版本解读:Iceberg 数据湖读取、Native ORC 输入格式与稀疏列窗口函数的稳定性修复

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ClickHouse v23.9.3.12-stable 版本解读:Iceberg 数据湖读取、Native ORC 输入格式与稀疏列窗口函数的稳定性修复

ClickHouse v23.9.3.12-stable 版本解读:Iceberg 数据湖读取、Native ORC 输入格式与稀疏列窗口函数的稳定性修复

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

v23.9.3.12-stable 是 ClickHouse 23.9 发布系列中的第二个补丁版本(相较于 v23.9.2.56-stable),本版本聚焦于三个用户可见的稳定性问题:Iceberg 存储文件检索、Native ORC 输入格式潜在的段错误,以及稀疏列(Sparse Columns)场景下的窗口函数计算。本文将结合当前仓库中的源码实现与测试用例,逐条拆解这三个修复背后的代码路径,帮助读者理解 ClickHouse 数据湖集成、列式格式解析与窗口执行引擎的工作原理,并学会如何在生产环境验证、升级与规避相关问题。

版本背景:changelog 的分类结构与本版定位

官方 release changelog 将改动分为两类:Bug Fix(用户可见的官方稳定版缺陷修复)NOT FOR CHANGELOG / INSIGNIFICANT(不面向终端用户、仅影响内部 CI 流程的改动)。本版本核心变更为:

分类改动PR
Bug Fix修复 Iceberg 存储文件检索(Fix storage Iceberg files retrieval)#55144
Bug Fix尝试修复 Native ORC 输入格式中可能的段错误(segfault)#55891
Bug Fix修复稀疏列场景下的窗口函数(window functions)#55895
NOT FOR CHANGELOG使用--filter减少 checkout 时间#54857
NOT FOR CHANGELOGPRInfodiff_urls的最后一处遗留清理#55874

其中两个“不进入 changelog”的条目均属于 ClickHouse 自动化 CI 基础设施(ci/jobs/下基于 praktika 框架的构建与合并流程),不涉及数据库运行时行为,因此本文重点围绕三个 Bug Fix 展开源码级分析。

修复一:Iceberg 存储文件检索(#55144)

Iceberg 在 ClickHouse 中的实现位置

ClickHouse 对 Iceberg 数据湖的支持并非独立目录,而是位于对象存储数据湖框架之下,核心代码集中在src/Storages/ObjectStorage/DataLakes/Iceberg/。从源码结构看,该目录覆盖了 Iceberg 集成的完整生命周期:

  • 元数据解析:IcebergMetadata.cpp、ManifestFile.cpp、Snapshot.h、MetadataGenerator.cpp 负责读取 Iceberg 表的元数据、快照(snapshot)、manifest 列表与 manifest 文件;
  • 文件遍历与剪枝:ManifestListPruning.cpp、ManifestFilesPruning.cpp、SnapshotFilesTraversal.cpp 实现基于 manifest 的谓词下推与文件级剪枝;
  • 路径与读取:IcebergPath.cpp、IcebergIterator.cpp、IcebergTableStateSnapshot.cpp 负责将 manifest 中记录的数据文件路径解析为可访问的对象存储路径并迭代读取;
  • 写入与治理:IcebergWrites.cpp、MultipleFileWriter.cpp、ExpireSnapshotsExecute.cpp 支持向 Iceberg 表写入以及快照过期治理。

“文件检索”问题可能涉及的环节

“Fix storage Iceberg files retrieval”这一修复标题所指向的,正是上述“manifest → 数据文件”的检索链路。Iceberg 规范下,一张表的数据文件清单并非直接列出,而是通过三层结构逐级定位:

  1. metadataJSON 文件指向当前快照(snapshot)
  2. 快照引用一个manifest list,其中每一项描述一个manifest file
  3. 每个 manifest file 中包含若干manifest entry,每条 entry 记录一个数据文件(data file)的路径、分区信息、统计信息与删除标记。

可以推断,该修复针对的是这三级检索链中数据文件路径的解析/拼接/过滤逻辑——例如在某种分区布局、路径编码或 manifest 变体下,数据文件未能被正确枚举或定位,从而导致查询结果缺失或报错。结合 ManifestFileIterator.cpp 与 IcebergIterator.cpp 的实现结构,修复大概率涉及这些迭代器在异常 manifest 状态下的容错与路径还原。

如何验证

在升级到 v23.9.3.12-stable 后,可以针对目标 Iceberg 表执行一次全量数据校验,例如:

-- 统计行数以确认所有数据文件都被检索到 SELECT count() FROM iceberg_s3('arn:aws:s3:::bucket/iceberg_db', 'db', 'table'); -- 对比分区维度 SELECT partition_col, count() FROM iceberg_s3('arn:aws:s3:::bucket/iceberg_db', 'db', 'table') GROUP BY partition_col;

若修复前存在“部分分区读不到数据”或“查询结果少于实际行数”的现象,修复后应恢复正常。

修复二:Native ORC 输入格式的潜在段错误(#55891)

Native ORC 输入格式的实现结构

“Native ORC”指的是 ClickHouse 直接基于 Apache ORC C++ 库(orc/OrcFile.hh)实现的输入格式,而非经由 Arrow 间接读取。其核心实现位于 src/Processors/Formats/Impl/NativeORCBlockInputFormat.h 与 src/Processors/Formats/Impl/NativeORCBlockInputFormat.cpp。从源码看,这一实现有四个值得注意的技术点:

  1. 自定义输入流适配层ORCInputStream将 ClickHouse 的SeekableReadBuffer适配为 ORC 库的orc::InputStream。构造函数(NativeORCBlockInputFormat.cpp#L80-L88)会根据缓冲是否支持readAt决定使用基于偏移的读取readBigAt,适用于需要随机访问 ORC 文件尾部信息的场景)还是 seek+read,并仅在调用方开启 prefetch 且缓冲区支持 read-at 时启用异步预取(use_async_prefetch),异步任务运行在ThreadName::ORC_FILE命名的 IO 线程池中。

  2. 段错误的高危区域:ORC 文件解析涉及尾部(PostScript/Footer)定位、stripe 元数据解析、ColumnVectorBatch内存分配与类型转换。该修复标题中的“possible segfault”,从代码路径可以推断最可能出现在两类场景:

    • 截断/损坏的输入文件ORCInputStream::readreadBigAt返回 0 字节时会抛出INCORRECT_DATA异常(见 NativeORCBlockInputFormat.cpp#L100-L118),这是防止越界读取导致的崩溃的关键保护。若此路径在修复前存在未覆盖的分支(例如某些文件尺寸校验缺失),就可能在解析损坏文件时触发段错误。
    • 内存池与取消标志getORCMemoryPool()确保 ORC 库的内存分配被 ClickHouse 的 MemoryTracker 记账、并在失败时抛出异常而非返回空指针被库解引用;is_stopped原子标志配合onCancel()处理查询取消。任何一个环节在并发/取消场景下的竞态都可能造成空指针解引用。
  3. 谓词下推与 stripe 级剪枝buildORCSearchArgument将 ClickHouse 的KeyCondition转换为 ORC 库的SearchArgument,实现查询下推到 ORC 的 row group 层面;calculateSelectedStripesskip_stripes用于跳过不需要读取的 stripe,这进一步说明 Native ORC 路径在读取时会与查询执行引擎深度交互。

  4. Schema 推断NativeORCSchemaReader实现ISchemaReader,负责从 ORC Footer 的类型树推断 ClickHouse 列类型,并读取行数(readNumberOrRows)。

修复思路与验证方式

段错误属于未定义行为,修复通常通过以下手段之一实现:补齐边界校验(如文件长度、stripe 索引越界)、修正空指针/悬垂引用、或在异步预取路径增加同步与生命周期管理。作为使用方,最直接的验证手段是:

-- 读取此前触发崩溃的 ORC 文件 SELECT * FROM file('corrupted.orc', 'ORC') LIMIT 10; -- 或直接以 ORC 格式做全量读取 SELECT count() FROM file('data.orc', 'ORC');

升级后应不再出现服务进程崩溃,而是以明确异常(如INCORRECT_DATA)的方式拒绝损坏文件。同时可在查询中开启输入格式的预取能力观察稳定性:

SELECT * FROM file('data.orc', 'ORC') SETTINGS input_format_orc_use_prefetch = 1;

关联测试佐证

仓库中的 src/Processors/tests/gtest_write_orc_iceberg_required.cpp 展示了 ORC 输入/输出格式的测试方法:通过ORCBlockOutputFormat写出文件、再用orc::createReader读回验证。其中ORCIcebergRequired.OptionalComplexFieldsAreNotRequired等用例覆盖了 Iceberg schema 到 ORC 类型树(含iceberg.required属性)的映射,NoInfoFallsBackToNullable则验证了无 Iceberg 元数据时的回退行为。可见 ClickHouse 对 ORC 与 Iceberg 的交互保持了一套完整的单元测试防线,任何对读取路径的改动都会被此类测试约束。

修复三:稀疏列场景下的窗口函数(#55895)

什么是稀疏列

ClickHouse 的稀疏列(Sparse Columns)是一种存储优化:当某列绝大多数行的值相同(默认值比例高于阈值)时,只存储非默认值及其位置,从而显著节省存储与 IO。相关行为在 src/Core/Settings.cpp 中有配置说明,且块级别的稀疏结构有专门测试 src/Core/tests/gtest_block_nested_sparse_structure.cpp 予以保障。

窗口函数执行引擎中的物化策略

窗口函数由 src/Processors/Transforms/WindowTransform.cpp 实现,它是一个流式ITransform,在内存中维护分区与帧状态。该文件 L352-L364 处有一段关键逻辑:

// We only need to materialize (remove Const/LowCardinality/Sparse from) the columns we actually // read while computing the window functions: the PARTITION BY and ORDER BY keys and the function // arguments. Everything else is passed through to the output untouched. should_materialize.assign(input_header.columns(), 0); for (const auto index : partition_by_indices) should_materialize[index] = 1; for (const auto index : order_by_indices) should_materialize[index] = 1; for (const auto & workspace : workspaces) for (auto argument_column_indice : workspace.argument_column_indices) should_materialize[argument_column_indice] = 1;

即:只对真正参与窗口计算的三类列(PARTITION BY 键、ORDER BY 键、函数参数)进行物化(去除 Const/LowCardinality/Sparse 包装),其余列原样透传。这是性能上的刻意取舍——物化稀疏列代价高昂,能省则省。

问题与修复机制

修复标题“Fix window functions in case of sparse columns”表明,在某种稀疏列输入组合下,窗口计算读取了未物化的稀疏列,导致取值错误、结果偏差甚至崩溃。从代码结构可以推断,修复的方式是让物化标记的判定与实际读取路径保持一致:凡是窗口函数真正需要读取值的列,都必须被纳入should_materialize;凡是透传列,则在帧计算中严格避免对其内容做假设。结合 L1421 附近“avoid paying unnecessary Const/LowCardinality/Sparse cost”的注释,可以确认该模块对这类包装列的处理是持续演进的,本次修复是该策略在稀疏列场景下的收口。

验证方式

-- 构造高默认值比例的稀疏列数据,再叠加窗口函数 SELECT key, row_number() OVER (PARTITION BY key ORDER BY ts) AS rn, sum(defaultish_col) OVER (PARTITION BY key) AS s FROM sparse_window_test ORDER BY key, ts;

升级后应确保:稀疏列参与 PARTITION BY/ORDER BY 或作为函数参数时结果与稠密列完全一致;稀疏列作为纯透传列时结果保持正确且不引入额外开销。

生产升级建议

  1. 版本选择前提:v23.9.3.12-stable 属于 23.9 发布线,适用于已经运行 23.9.x 且受上述三个问题影响的用户;若尚未使用 23.9 系列,建议评估当前维护线的最新补丁版本。本文所述行为以当前仓库源码为准。
  2. 回归清单:升级后重点回归三类场景——Iceberg 表查询(含分区剪枝与数据文件检索)、ORC 文件导入(含损坏文件与预取开关)、以及包含窗口函数且涉及稀疏列的报表查询。
  3. 监控与回滚:关注system.changelog或发布注记中的后续修复;如升级后出现新问题,可通过官方构建包回退到上一补丁版本。

小结

v23.9.3.12-stable 是一个典型的“小而稳”的补丁版本:三个 Bug Fix 分别落在 ClickHouse 的数据湖集成(Iceberg 文件检索)、列式格式解析(Native ORC 段错误)与执行引擎(稀疏列窗口函数)三条关键链路上,对应源码分别位于 src/Storages/ObjectStorage/DataLakes/Iceberg/、src/Processors/Formats/Impl/NativeORCBlockInputFormat.cpp 与 src/Processors/Transforms/WindowTransform.cpp。理解这些修复背后的代码路径,不仅有助于评估升级影响,也能为读者在自建场景中排查同类问题提供直接线索——无论是 Iceberg 数据文件的检索异常、ORC 损坏文件的防御式解析,还是稀疏列与窗口函数的交互边界。

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 11:19:13

SpringBoot与SSM框架构建超市POS收银系统实战

1. 项目概述:超市POS收银管理系统的核心价值超市POS收银系统是零售行业最基础也最核心的数字化工具。基于SpringBoot和SSM框架开发的这套系统,本质上是通过技术手段将传统人工收银、库存管理、销售统计等业务流程标准化、自动化。我在实际部署中发现&…

作者头像 李华
网站建设 2026/9/15 11:18:49

Python Sass 样式预处理 实战:安装、配置与生产部署验收

Python Sass 样式预处理 实战:安装、配置与生产部署验收工具地址:https://www.speedce.com 社区论坛:https://bbs.speedce.com 联系:speedceadsgmail.com写在前面 围绕「Sass 样式预处理」,本文提供可落地的技术指南&a…

作者头像 李华