尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ClickHouse v26.3.5.12-lts 版本深度解析:INSERT 去重性能修复与七项关键 Bug 修复实战指南

ClickHouse v26.3.5.12-lts 版本深度解析:INSERT 去重性能修复与七项关键 Bug 修复实战指南 ClickHouse v26.3.5.12-lts 版本深度解析INSERT 去重性能修复与七项关键 Bug 修复实战指南【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse导读本文以 ClickHouse 官方变更日志 docs/changelogs/v26.3.5.12-lts.md 为骨架逐项解析 v26.3.5.12-lts相对上一版 v26.3.4.11-lts引入的 1 项性能修复、2 项改进与 7 项用户可见的 Bug 修复。文中不仅还原每项变更的触发场景与影响范围还深入对应源码src/Core/Settings.cpp、src/Interpreters/InsertDeduplication.cpp、src/Functions/splitByString.cpp等揭示底层实现原理。读完本文你将理解deduplicate_insert性能回归的来龙去脉、splitByString与formatDateTime行为变更的兼容性影响以及如何为升级该 LTS 版本做好针对性验证。版本概览v26.3.5.12-lts 修复了什么本版本是基于 v26.3 分支的 LTS 补丁版本变更范围集中在四类分类数量主题Performance Improvement1deduplicate_insertenable下的 INSERT 性能回归修复Improvement2启动时缓存加载优化、mongo-c-driver 依赖升级Bug Fix用户可见7JOIN 缓存、物化视图、Delta Lake、Object 动态路径、formatDateTime、UDF 注册表、splitByStringNOT FOR CHANGELOG1leftPad/rightPad堆缓冲区溢出修复其中绝大多数 Bug 修复均为backport从 master 分支回移植入 LTS 分支因此本版本的升级风险较低但splitByString的行为变更属于用户可见的兼容性调整升级前应评估存量查询。性能修复deduplicate_insert导致的 INSERT 性能回归问题背景26.2 起默认开启的块去重自 26.2 版本起deduplicate_insert的设置默认值变更为enable。该设置在 src/Core/Settings.cpp 中定义DECLARE(DeduplicateInsertMode, deduplicate_insert, DeduplicateInsertMode::ENABLE, R( Enables or disables block deduplication of INSERT INTO (for Replicated\* tables). The setting applies to both synchronous and asynchronous inserts, and it supersedes the insert_deduplicate and async_insert_deduplicate settings. That setting has three possible values: - disable — Deduplication is disabled for INSERT INTO query. - enable — Deduplication is enabled for INSERT INTO query. - backward_compatible_choice — Deduplication is enabled if insert_deduplicate or async_insert_deduplicate are enabled for specific insert type. ), 0)三个取值的关键差异disable完全关闭INSERT INTO的块去重enable无论同步还是异步插入均强制开启去重backward_compatible_choice仅在insert_deduplicate同步插入或async_insert_deduplicate异步插入单独开启时才去重用于保持旧版本行为。对于INSERT SELECT查询则优先参考deduplicate_insert_selectsrc/Core/Settings.cpp其取值force_enable/enable_when_possible/enable_even_for_bad_queries/disable决定了 SELECT 结果不稳定未携带ORDER BY ALL且非单流或未提供insert_deduplication_token时的降级策略。回归根因数据哈希计算时机去重需要为每个插入块计算数据哈希再与去重日志deduplication_hashes目录比对。v26.3.4.11-lts 及更早版本中该哈希计算发生在squashing块合并压缩阶段且采用逐行per-row哈希的方式。对于大块插入squashing 阶段的计算开销会被显著放大。实测数据表明5M 行、22 列的数据块哈希计算开销约为 2.5 秒这对于高频批量写入场景是不可接受的回归。修复方案推迟到 Sink 阶段 批量列哈希修复PR #101494采取了两项组合手段推迟哈希计算时机将数据哈希的计算从 squashing 阶段延迟到写入 Sink 阶段避免在压缩/合并路径上重复或过早地支付哈希成本批量列哈希改用列式批量接口updateHashWithValueRange替代逐行哈希。updateHashWithValueRange的实现在 src/Columns/IColumn.h 中声明并由各列类型实现如 src/Columns/ColumnVector.cpp、src/Columns/ColumnString.cpp、src/Columns/ColumnArray.cpp 等。其核心思想是在单次调用中更新[begin, end)行区间内所有值的哈希状态充分利用列式存储的连续内存布局避免逐行虚拟调用。在 src/Interpreters/InsertDeduplication.cpp 的DeduplicationInfo::calculateDataHashes()中可以清晰看到新的批处理流程/// Column-major so only one column is materialized at a time, instead of a dense copy of the whole block. std::vectorSipHash hashes(pending.size()); for (const auto col : original_block-getColumns()) { auto dense col-convertToFullIfWrapped(); for (size_t i 0; i pending.size(); i) dense-updateHashWithValueRange(getTokenBegin(pending[i]), getTokenEnd(pending[i]), hashes[i]); }该实现有三个值得注意的细节列主序column-major遍历任一时刻仅物化一列避免为整个块创建稠密副本降低内存峰值convertToFullIfWrapped()去包装哈希必须与列的表示形式无关因此需要剥离ColumnConst、ColumnSparse等表示层包装否则同一数据两次插入会因包装层产生不同哈希导致重试插入无法去重。LowCardinality是语义类型而非表示形式故刻意保留ProfileEvents::DuplicationDataHashComputations计数每次计算的 token 数量都会被记录便于观测去重哈希计算的实际开销src/Interpreters/InsertDeduplication.cpp。效果与验证修复后同样的 5M 行 / 22 列数据哈希计算开销从约 2.5 秒降至约 0.5 秒。除性能外本次重构还统一了同步与异步插入的去重哈希路径——从DeduplicationHash::getPath()src/Interpreters/InsertDeduplication.cpp可以看到SYNCblocks目录、ASYNCasync_blocks目录与UNIFIEDdeduplication_hashes目录三种哈希存储路径统一哈希正是为后续合并去重日志目录做铺垫。升级验证建议在 Replicated 表上执行大数据量INSERT对比升级前后的耗时同时可通过system.metric_log中的DuplicationDataHashComputations指标观测哈希计算频率与规模。改进项启动缓存加载优化与依赖升级服务器启动时的缓存加载优化PR #101500在服务启动阶段各类缓存如表结构缓存、字典缓存、远程文件系统元数据缓存等的加载若串行或低效执行会显著拉长不可用窗口。本改进优化了缓存加载路径降低启动延迟。对于大规模集群缩短启动时间意味着故障恢复与滚动升级期间的服务中断时间更短。mongo-c-driver 升级 2.2.2 → 2.2.3PR #101655仓库在contrib/目录以子模块方式管理第三方依赖如contrib/mongo-c-driver/与contrib/mongo-c-driver-cmake/。本次将 MongoDB 官方 C 驱动从 2.2.2 升级到 2.2.3属于补丁级修复版本。该变更影响使用 MongoDB 字典源、mongo表函数等依赖 Mongo C 驱动能力的用户建议在升级后回归验证相关查询。七项 Bug 修复逐项解析1. JOIN shard-by-PK 优化 查询条件缓存导致的错误结果PR #100926场景当 JOIN 启用 shard-by-PK 优化且查询条件缓存query condition cache生效时若部分数据 part 被缓存条件过滤掉JOIN 结果可能错误。原因shard-by-PK 优化依赖按主键对 JOIN 键进行分片裁剪而条件缓存可能复用了不适用于当前 part 集合的裁剪结果——被过滤掉的 part 本应参与 JOIN却因缓存条件被排除。影响返回不完整或错误的 JOIN 结果属于典型的静默错误比报错更危险。升级验证建议在分布式表 Replicated 表上构造带ON条件引用主键列的 JOIN 查询配合条件缓存命中场景对比升级前后的结果集一致性。2. 异步启动时物化视图Target table doesnt exist错误PR #100946场景服务器异步启动async startup期间物化视图Materialized View依赖其内部表inner table而启动依赖顺序错误会导致内部表尚未就绪时视图已开始初始化。原因启动依赖排序startup dependency ordering不正确视图在其目标内部表创建完成前被尝试加载。影响出现Target table doesnt exist错误导致视图在启动阶段加载失败。升级验证建议在包含大量物化视图的实例上执行多次重启确认启动日志中不再出现该错误并检查system.tables中视图状态正常。3. Delta Lake 集群 ReplicatedMergeTree 的 insert-select 失败PR #101299场景从 Delta Lake 集群执行INSERT ... SELECT写入 ReplicatedMergeTree 表时失败。影响数据湖Delta Lake与复制表之间的数据流转链路中断。升级验证建议使用deltaLake表函数或DeltaLake表引擎执行 insert-select验证跨引擎写入正常且数据一致。4.Object类型动态路径并行反序列化中的 use-after-scopePR #101823场景读取包含大量动态路径dynamic paths的Object类型表时并行反序列化路径存在 use-after-scope悬垂引用问题。原因并行反序列化中某些路径引用的临时对象生命周期提前结束形成悬垂引用。影响读取此类表可能直接崩溃crash尤其在动态路径数量众多时触发概率更高。升级验证建议构造含数百个动态路径的Object类型列使用多线程读取并反复扫描确认无崩溃。5.formatDateTime的%W格式化输出错误PR #101847场景formatDateTime使用%W格式符输出完整的星期名称如 Monday-Sunday见 src/Functions/formatDateTime.cpp 中的格式符文档表时在某些非默认格式化设置下输出错误。原因%W的星期名称解析依赖本地化/格式化上下文非默认设置组合下分支路径存在缺陷。影响日期时间格式化结果错误影响报表、日志处理等下游消费。升级验证建议-- 默认设置 SELECT formatDateTime(toDateTime(2026-09-18 10:00:00), %W); -- 对比非默认设置如不同的时区/方言设置 SELECT formatDateTime(toDateTime(2026-09-18 10:00:00), %W, UTC);升级后应回归验证所有使用%W的生产查询。6. ZooKeeper 会话过期导致 UDF 注册表丢失PR #101891场景UDF用户自定义函数注册表依赖 ZooKeeper 存储。当 ZooKeeper 会话在周期刷新期间过期UDF 注册表内容可能整体丢失所有用户自定义函数在完整刷新成功前不可用。原因会话过期时周期刷新逻辑未保留已有注册数据刷新失败即丢失全部 UDF 状态。影响UDF 集体失效依赖 UDF 的查询全部报错直到下一次完整刷新成功。升级验证建议在测试环境模拟 ZooKeeper 会话过期重启 ZooKeeper 或网络分区确认 UDF 在会话恢复后立即可用无需手动重建。7.splitByString拒绝空分隔符PR #101928——行为变更需重点关注这是本版本中最需要关注的行为变更。变更内容splitByString分词器现在拒绝空分隔符字符串。在此之前空分隔符会触发将字符串拆分为单字符数组的隐式行为——在 src/Functions/splitByString.cpp 中可以明确看到该分支if (separator.empty())时按单字符逐个产出 token。底层实现splitByString(separator, s[, max_substrings])要求第一个参数为常量字符串getArgumentsThatAreAlwaysConstant()强制{0, 2}恒定为常量见 src/Functions/splitByString.cppinit()中通过checkAndGetColumnConstStringOrFixedString校验src/Functions/splitByString.cpp。修复后空分隔符在入口即被拒绝不再进入单字符拆分逻辑。升级影响-- 修复前隐式按单字符拆分 SELECT splitByString(, hello); -- [h, e, l, l, o] -- 修复后直接报错 SELECT splitByString(, hello); -- 异常Empty separator迁移建议若存量业务依赖空分隔符实现字符拆分请改用splitByChar(, s)或char相关函数显式表达意图避免升级后查询报错。NOT FOR CHANGELOGleftPad/rightPad堆缓冲区溢出修复PR #101951虽然该修复未列入正式变更日志属于内部安全/稳定性修复但其重要性不低。场景leftPad/rightPad函数在特定输入组合下存在堆缓冲区溢出heap-buffer-overflow。实现位置src/Functions/padString.cpp 中的FunctionPadString注册为leftPad、leftPadUTF8、rightPad等见 src/Functions/padString.cpp其中lpad是leftPad的不区分大小写别名。leftPadUTF8与leftPad的区别在于前者按 Unicode 码点而非字节测量字符串长度。影响可能触发 ASAN/UBSAN 检测或导致进程崩溃在极端场景下缓冲区越界读写存在被恶意利用的潜在风险。任何使用leftPad、rightPad、leftPadUTF8、rightPadUTF8及别名lpad、rpad的查询都应纳入升级回归范围。升级与回归验证清单针对 v26.3.5.12-lts 的升级建议按以下清单执行回归写入路径Replicated 表大数据量 INSERT覆盖同步/异步关注耗时与DuplicationDataHashComputations指标验证deduplicate_insert性能修复字符串函数全量回归splitByString重点检查是否存在空分隔符用法、leftPad/rightPad及其 UTF8 变体、formatDateTime的%W格式数据湖与 JOINDelta Lake insert-select、带条件缓存的 JOIN 查询启动与依赖含物化视图实例的多轮重启、ZooKeeper 会话过期场景下的 UDF 可用性Object 类型多动态路径表的多线程读取压力测试。对于升级后的行为变更splitByString建议先在灰度环境验证存量查询兼容性再逐步推广至全集群。【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表