ClickHouse v26.3.3.20-lts 缺陷修复详解:异步插入计数、备份路径安全与 12 项稳定性补丁
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
导读
本文基于当前仓库 docs/changelogs/v26.3.3.20-lts.md 展开,逐条解析 ClickHouse v26.3.3.20-lts(commit 5342e88d453)相对 v26.3.2.3-lts(commit a1a0cf365ce)的 20 项变更。本版本属于 LTS 分支的缺陷修复(Bug Fix)迭代,重点覆盖异步插入(async insert)统计口径、备份恢复(RESTORE)路径校验安全加固、session_timezone解析一致性、以及字典卸载与文本检索索引等多处崩溃场景。读完本文,你将掌握每个修复的触发场景、修复思路、可复现的 SQL 形态,以及对应源码位置,便于自行验证与回归测试。
一、版本概览与定位
v26.3.3.20-lts 是一个典型的LTS 分支缺陷修复版本:不引入新功能,专注于修复官方稳定版中用户可见的错误行为(user-visible misbehavior)。变更清单可归为三类:
| 变更分类 | 数量 | 说明 |
|---|---|---|
| Bug Fix | 14 | 官方稳定版中用户可见的错误行为修复 |
| NO CL CATEGORY | 1 | 未归类(缺少 changelog 分类标签)的修复 |
| NOT FOR CHANGELOG / INSIGNIFICANT | 5 | 内部修复或非显著变更,不面向用户公告 |
从修复分布看,本版本的高频主题集中在三块:异步插入(async insert)链路(计数、时区解析、结果行数)、备份/恢复(Backup/RESTORE)路径安全、以及服务器运行期崩溃(字典卸载、ULID 解析、文本检索索引等)。
二、异步插入(Async Insert)链路的三项修复
异步插入是 ClickHouse 为高吞吐小批量写入设计的机制:客户端以async_insert=1提交写入,服务端先将数据缓冲,再合并为较大批次落盘。本版本针对该链路修复了三处一致性问题。
2.1 修复 query_log 与客户端输出中的零统计
变更:修复异步插入在query_log与客户端输出中上报written_rows、read_rows、result_rows全为 0 的问题(PR #99879)。
影响面:所有使用async_insert=1的写入用户。此前查询日志中的行数统计失真,会干扰基于query_log的监控、审计与计费统计。
源码印证:异步插入的启用与回退逻辑集中在 src/Interpreters/executeQuery.cpp 的executeQuery路径中,涉及async_insert、wait_for_async_insert、wait_for_async_insert_timeout三个设置的读取,以及数据量过大时同步执行的降级判断(LOG_DEBUG(logger, "Setting async_insert=1, but INSERT query will be executed synchronously..."))。本次修复正是补全该链路上行数统计的传递。
2.2 修复 HTTP 与 TCP 异步插入忽略 session_timezone
变更:修复session_timezone在异步插入(TCP 协议)以及所有 HTTP 协议插入场景中被忽略、导致DateTime值解析错误的问题(PR #100647)。
影响面:依赖session_timezone会话设置解析字符串时间戳的用户。此前同一 SQL 在同步 TCP 插入与异步/HTTP 插入下可能得到不同的时间存储值,属于典型的一致性问题。
修复思路:确保解析DateTime时统一携带会话时区上下文,而非退化为默认时区。
2.3 修复结果行数重复计数
变更:修复结果行数(result rows)被重复计算的问题(PR #101226),与 2.1 同属统计口径修复,均由异步插入路径的计数逻辑引起。
三、备份恢复(RESTORE)路径校验安全加固
变更:在恢复过程中校验备份元数据中的文件条目路径,拒绝路径穿越(path traversal)、绝对路径与空名称(PR #100483)。
风险场景:备份文件可能被篡改或损坏(corrupted/tampered backup)。若元数据中的文件路径未经校验直接用于写盘,恶意或损坏的备份可在恢复时把文件写到预期目录之外,构成写任意路径的漏洞。该问题由 Pablo Marcos 报告并修复。
源码印证:校验函数validateFileNameFromBackup实现在 src/Backups/BackupImpl.cpp,校验规则完整覆盖文档所述三类攻击面:
- 绝对路径/根路径:
path.is_absolute() || path.has_root_name() || path.has_root_directory()任一成立即抛INSECURE_PATH; - 路径穿越:经
path.lexically_normal()归一化后,若首段为..(即解析后逃出备份根目录)则拒绝; - 空名称/退化路径:归一化后为空或
.时抛BACKUP_DAMAGED; - 附加校验:要求路径本身已是归一化形式(
normalized.string() == file_name),避免a//b这类非归一化写法绕过前缀裁剪逻辑。
该校验分别应用于备份条目主文件(info.file_name)与数据文件(info.data_file_name),覆盖了 src/Backups/BackupImpl.cpp 与 src/Backups/BackupImpl.cpp 两处入口。
四、服务器运行期崩溃与逻辑错误修复
4.1 字典加载与服务器关闭的并发竞态
变更:修复关闭服务器时加载字典触发的空指针解引用(segfault)。根因是字典线程并发调用Context::getUserDefinedSQLObjectsStorage(解引用user_defined_sql_objects_storage),而主线程的Context::shutdown会将该字段置空(PR #100839)。
修复思路:在Context::shutdown之前,先禁用字典加载器的后续更新、终止正在运行的字典查询并 join 字典加载线程——即与普通查询的关闭流程对齐,保证共享上下文在卸载前不再被并发访问。
4.2 ULIDStringToDateTime 对非 ASCII 输入的缓冲区溢出
变更:修复ULIDStringToDateTime在输入包含非 ASCII 字节时的缓冲区溢出(PR #100843)。
源码印证:该函数位于 src/Functions/ULIDStringToDateTime.cpp,接受String或FixedString(26)输入并返回毫秒精度的DateTime64(3)。其解码函数decode现在先在 src/Functions/ULIDStringToDateTime.cpp 中对 26 字节逐一校验:
校验所有字节均为 ASCII 后再调用
ulid_decode,因为ulid_decode以字符值作为数组下标——带符号 char 值 ≥ 128 会产生负下标,导致越界读取。
任何非 ASCII 字符(data[i] >= 128)都会抛出BAD_ARGUMENTS,例如:
SELECT ULIDStringToDateTime('01GNB2S2FGN2P93QPXDNB4EN2R'); -- 正常解析 -- 非法字符输入将得到:Cannot parse ULID: non-ASCII character at position N该函数自 v23.3 引入(IntroducedIn = {23, 3}),建议升级后对包含脏数据、编码混用的 ULID 列做回归验证。
4.3 文本检索索引与元组子查询 IN 的崩溃
变更:修复使用文本检索索引(text search index)时,IN子句包含元组子查询导致的崩溃,例如WHERE (id, str) IN (SELECT (id, str) FROM ...),以及子查询列数与左侧元组不匹配的场景(PR #100959)。
影响面:对带全文/文本索引的表执行复合键IN (SELECT ...)查询的用户。
4.4 has() + PREWHERE/WHERE 与 LowCardinality 元组键崩溃
变更:修复has()函数配合 PREWHERE/WHERE、作用于包含 LowCardinality 元素的元组键时导致的服务器崩溃(PR #100760)。
4.5 intDiv / intDivOrZero 与 Nullable 元组数组异常
变更:修复intDiv/intDivOrZero作用于可空元组数组时的异常,文档给出复现形态:
SELECT intDiv([divide((1, 2), ... AND NULL)], 2)(PR #100895)建议在升级后用含NULL的嵌套元组数组数据对该类函数做回归测试。
4.6 缓存盘"Having zero bytes"逻辑错误
变更:修复缓存相关逻辑错误Having zero bytes, ...。根因是远端对象在list与read之间被覆盖,导致读取到过期对象元数据(PR #101219),典型发生于共享存储 + 本地缓存盘场景。另有一项同主题修复(PR #101230)随本版本合入。
五、聚合与优化器相关的兼容性修复
5.1 sumCountOrDefault 旧序列化状态读取失败
变更:修复sumCountOrDefault聚合函数在含一个及以上Nullable参数时,因Nullable(Tuple)类型的引入而无法读取旧版本序列化状态的问题(PR #101021,关闭 issue #100882)。
影响面:升级前写入的、使用sumCountOrDefault的物化视图或AggregatingMergeTree分区数据。不修复会出现跨版本状态反序列化失败。
源码印证:该函数注册于 src/AggregateFunctions/registerAggregateFunctions.cpp,实现位于 src/AggregateFunctions/AggregateFunctionSumCount.cpp。升级 LTS 后建议对存量聚合状态做一次读取冒烟测试。
5.2 StorageAlias 引擎参数求值时机
变更:StorageAlias的引擎参数在保存表定义前先求值,使currentDatabase()这类表达式在写入数据库前被解析为字面量(PR #100902)。此前若在非默认库中重建依赖该别名表的对象,可能得到错误解析结果。
5.3 optimizeLazyMaterialization 与投影 PREWHERE 异常
变更:修复optimizeLazyMaterialization在使用含 PREWHERE 的投影(projection)且带ORDER BY ... LIMIT时抛异常的问题(PR #101115),属于查询优化器在投影 + 惰性物化组合路径下的边界修复。
六、NO CL CATEGORY 与内部修复
6.1 NO CL CATEGORY
- PR #101241(Anton Popov):一项未附 changelog 分类标签的修复,随本版本合入。
6.2 NOT FOR CHANGELOG / INSIGNIFICANT(内部修复)
该分类用于不面向用户公告、但对系统健壮性有实际意义的内部变更:
| 变更 | 内容 | 说明 |
|---|---|---|
| PR #99741 | 修复缓存盘(cache disk)启动逻辑 | 此前启动时跳过了访问检查(access check),存在权限语义偏差 |
| PR #100064 | 修复 issue #99996 | 内部行为修正 |
| PR #100522 | removeObjectsIfExist由顺序删除改为批量删除 | 涉及对象存储清理路径的 I/O 优化 |
| PR #100907 | 配置重载后正确重新计算位置(locations) | 修复热更新配置后缓存位置未同步的问题 |
| PR #101226 | 修复结果行数重复计数 | 与 2.3 同一修复,此分类下重复列出 |
七、升级与验证建议
- 统计口径回归:异步插入用户应检查
query_log中written_rows/read_rows/result_rows是否恢复正常,可通过system.query_log对比同步与异步插入的统计差异。 - 备份安全:建议在升级后执行一次包含相对路径、嵌套目录的
BACKUP/RESTORE往返测试,确认 src/Backups/BackupImpl.cpp 中的路径校验不影响正常备份;同时可利用validateFileNameFromBackup的报错信息(INSECURE_PATH/BACKUP_DAMAGED)识别异常备份。 - 时区一致性:设置
session_timezone后分别通过 HTTP 与 TCP、同步与异步方式插入同一DateTime字符串,比对存储结果是否一致。 - 崩溃类回归:覆盖
has()+ LowCardinality 元组键 PREWHERE、文本检索索引 + 元组子查询IN、ULIDStringToDateTime非 ASCII 输入、intDiv+ Nullable 元组数组四类场景,确认不再抛异常或崩溃。 - 聚合状态兼容:对存量
sumCountOrDefault(Nullable)状态执行读取验证,确认新版本可正常反序列化旧状态。
结语
v26.3.3.20-lts 虽是一个纯修复版本,但其价值集中在三类高风险面上:异步插入的统计与时区一致性、RESTORE 的路径安全边界,以及多个并发/越界崩溃点。对生产环境而言,上述第 3.2(备份路径校验)与 4.2(ULID 溢出)两项具备安全属性,建议优先评估升级;统计口径类修复则适合作为监控告警基线校正的依据。所有修复均可对照本文给出的源码路径,在 docs/changelogs/v26.3.3.20-lts.md 的原始条目中找到对应 PR 号,进行二次核验。
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考