
ClickHouse v20.12.8.5-stable 补丁版解读字典缺席键、聚合组合器与并发一致性的五处 Bug 修复【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse本篇文章围绕官方补丁版v20.12.8.5-stable的发布记录展开逐一剖析该版本相对v20.12.7.3-stable修复的五处缺陷字典缺席键查询行为不一致、-Distinct与-State聚合组合器组合使用异常、ALTER MODIFY COLUMN生成必然失败的 mutation、StorageJoin 并发插入竞争以及 INSERT 回调竞态导致的重复数据。读者在读完本文后将能准确理解这些修复的背景、触发条件、底层原理并知道如何在当前仓库源码src 目录与测试tests 目录中定位与验证同类问题。版本全景一次聚焦正确性与一致性的小版本修复v20.12.8.5-stable属于 ClickHouse 20.12 LTS 系列的维护补丁版全部改动均为Bug Fix缺陷修复不包含新功能、不改变公开配置格式与数据格式因此对已部署 20.12 系列的实例属于低风险升级。五处修复覆盖了三个核心领域修复编号PR修复领域一句话概括#20578字典Dictionary查询缺席键时字典行为不一致#20866聚合函数组合器-Distinct与-State组合器配合使用时出错#21007ALTER 与 MutationALTER MODIFY COLUMN可能创建必然失败的 mutation#21009存储引擎并发阻止对 StorageJoin 的并行插入#21138INSERT 写入路径回调迟到导致插入出现重复数据从修复主题看该版本重点关注数据查询结果的确定性字典、聚合与写入/变更操作的并发安全JOIN 表、INSERT 回调这正是分析型数据库在读多写少场景下最容易暴露的一致性盲区。字典查询缺席键absent key的行为一致性修复修复内容此前在字典查询中查找**不存在的键absent keys**时字典返回结果可能不一致——取决于查询的构造方式例如是否经过dictGet的常量折叠、是否命中缓存路径等对同一个缺席键可能返回默认值、NULL 或抛出异常等不同结果。本修复统一了缺席键的处理语义。底层机制ClickHouse 的字典实现位于 src/Dictionaries约 140 余个源文件涵盖哈希字典、范围字典、缓存字典、SSD 缓存字典等多种实现。缺席键的处理差异往往来自两个层面命中路径不同字典既有直接内存查找路径也有缓存未命中后回源加载路径cache与ssd_cache字典两条路径对缺失键的缺省值回填逻辑必须一致NULL 与默认值语义dictGet系列函数对缺席键应返回default参数指定的默认值而dictGetOrDefault等变体语义不同若底层 get 函数返回异常码处理不一致就会表现为行为漂移。排查建议若在 20.12 早期版本中遇到同一张字典表、同一把缺席键dictGet结果时有时无的现场应优先检查是否使用了cache或ssc_cache布局命中/未命中路径差异最大查询中是否混用了dictGet、dictGetOrDefault、dictGetOrNullsrc/Dictionaries 中同名函数族它们对缺席键的返回约定本就不同字典LIFETIME刷新期间缓存字典的行为。该修复即保证了无论走哪条查找路径缺席键的返回结果保持一致。聚合组合器-Distinct 与 -State 的组合修复修复内容修复了在聚合函数中同时使用-Distinct与-State组合器时的错误行为。典型写法如sumDistinctState(x)它要求先按唯一值去重再输出未终结的聚合状态state以便后续通过-Merge组合器在多个分片/线程间合并。源码级原理在 src/AggregateFunctions/Combinators 中这两个组合器由两个独立适配器实现AggregateFunctionDistinctAggregateFunctionDistinct.h是一个包装适配器内部持有nested_func用哈希集合HashSet维护已见过的值历史仅把首次出现的行转交给被包装函数处理。源码注释明确说明其定位是Adaptor for aggregate functions. Adding -Distinct suffix to aggregate functionAggregateFunctionStateAggregateFunctionState.h同样是适配器Aggregate functions with theStatesuffix differ from the corresponding ones in that their states are not finalized即不终结状态、直接输出DataTypeAggregateFunction类型的序列化状态。当二者叠加为xxxDistinctState时关键问题出现在状态合并merge环节。在 AggregateFunctionDistinct.h 中可以看到Distinct 适配器实现了canMergeStateFromDifferentVariant/mergeStateFromDifferentVariant这对接口——它允许在不读取对方嵌套函数状态的情况下仅凭唯一值历史合并两个不同的状态变体其前提正是 Distinct 状态自包含完整去重历史。而AggregateFunctionState适配器AggregateFunctionState.h通过getNestedFunction()将合并请求逐级下探到被包装函数。该版本修复的正是这两级适配器叠加时嵌套函数状态落位nested place 偏移或合并语义在特定组合下不正确的问题。验证方式可直接使用形如以下的查询验证组合器行为结果应等价于sumDistinct且不抛异常SELECT sumDistinctState(x) AS s, sumMerge(s) AS total FROM (SELECT arrayJoin([1, 2, 2, 3, 3, 3]) AS x); -- 期望结果total 6仅 123重复值被去重ALTER MODIFY COLUMN避免创建必然失败的 mutation修复内容修复了ALTER MODIFY COLUMN可能创建出注定会执行失败的 mutation 的行为。此前某些不合法或不合理的列类型修改例如把列改为与现有数据/SORTING KEY 冲突的类型会先被受理、生成 mutation 任务直到后台执行阶段才报错导致 mutation 长期卡在队列中反复失败。相关实现在 src/Storages/MutationCommands.cpp 中可以看到 mutation 命令模型的骨架MutationCommand定义了RENAME_COLUMN、DROP_COLUMN、DELETE、APPLY_DELETED_MASK、REWRITE_PARTS等类型并区分isBarrierCommand()屏障命令、isPureMetadataCommand()纯元数据变更、affectsAllColumns()影响全列等特性。ALTER MODIFY COLUMN这类改列操作会先解析进 mutation 命令序列再在后台由 Mutation 执行器逐 part 应用。若类型校验推迟到执行阶段就会出现mutation 已被记录、但必然失败的脏状态。修复价值让不兼容的列修改在提交阶段即被拒绝而不是污染 mutation 队列。对运维实践的直接启示是在升级到本版本后ALTER TABLE ... MODIFY COLUMN的错误反馈时机会更早、更明确修改列类型前应先确认目标类型与原数据兼容且与表的ORDER BY/ 主键 / 分区键约束不冲突若历史版本中已存在失败的 mutation可查询system.mutations观察is_done、latest_failed_part升级后可清理或重建受影响 part。StorageJoin阻止并行插入保护哈希表结构修复内容阻塞对StorageJoinJOIN 引擎表的并行插入操作。Join 引擎表将内存中的哈希表作为连接状态若多个线程同时对同一张 Join 表执行 INSERT哈希表结构可能在中途被修改导致正在进行的查询读到不一致甚至损坏的中间状态。源码印证在 src/Storages/StorageJoin.h 中可以找到明确的设计注释第 51–52 行说明Return instance of HashJoin holding lock that protects from insertions to StorageJoin. HashJoin relies on structure of hash table thats why we need to return it with locked mutex.——即返回HashJoin时必须持有保护插入的锁因为HashJoin依赖哈希表结构的一致性第 59–62 行说明joinGet(OrNull)处理数据块时会获取读写锁rwlock的读侧以防止在处理数据块期间发生并行的 StorageJoin 更新。可见读要加锁、写要互斥是本引擎的并发模型核心。本修复将并行插入也纳入同一互斥保护范围从源头杜绝边写边读哈希表的竞态。实践启示JOIN 引擎表适合低频批量装载、高频joinGet点查的在线服务场景。若业务需要在运行时持续更新 Join 表应串行化写入或改用其他支持并发的连接方案升级到本版本后并行 INSERT 会被阻塞/串行化这是行为上的有意收紧不是回归。INSERT 重复数据回调迟到导致的竞态修复修复内容修复了 INSERT 查询可能产生重复数据的缺陷——当某个异步/回调类回调来得稍晚时写入路径可能把同一批数据重复提交造成表中出现重复行对应 issue #21112。触发场景该问题常见于依赖回调确认的写入链路例如分布式表Distributed异步发送、带确认回调的异步插入async insert、或外部存储/Filesystem 类目标的回调式提交。回调到达时序不确定时若主流程与回调都各自执行了一次提交就会重复写入。修复价值与验证修复后即使在回调延迟到达的边界条件下同一批数据也只提交一次。验证手段可参考仓库中的查询测试体系tests/queries构造高并发/异步 INSERT 场景写入后对目标表执行去重计数对比确认无重复行。升级与回归验证建议适用范围仅针对 20.12 系列v20.12.x-stable实例若当前运行版本低于v20.12.8.5-stable建议在测试环境先行验证后升级。回归重点结合本版本修复面重点回归——字典缺席键查询结果、含DistinctState的聚合查询、ALTER TABLE ... MODIFY COLUMN的报错行为、JOIN 引擎表的写入与joinGet查询、以及分布式/异步 INSERT 的重复数据检查。源码与测试定位聚合组合器查看 src/AggregateFunctions/Combinators 与 src/AggregateFunctions/Combinators/AggregateFunctionDistinct.cpp字典实现查看 src/Dictionaries变更命令查看 src/Storages/MutationCommands.cpp存储引擎查看 src/Storages/StorageJoin.h回归测试在 tests/queries 中检索Distinct、dictGet、MODIFY COLUMN等关键词对应的.sql用例。总体而言v20.12.8.5-stable是一个典型的稳定性补丁版不引入新功能但系统性修复了字典查询、聚合组合器、ALTER 变更、JOIN 引擎并发与 INSERT 竞态五类问题为 20.12 长期支持系列的生产稳定性补上了关键拼图。【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考