ClickHouse v20.8.15.11-lts 版本详解:分布式连接、Mutation 约束与复制表并发修复全解析
2026/9/10 10:01:47 网站建设 项目流程

ClickHouse v20.8.15.11-lts 版本详解:分布式连接、Mutation 约束与复制表并发修复全解析

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

v20.8.15.11-lts 是 ClickHouse 20.8 LTS(长期支持)分支的又一个 Bugfix 版本,相比前序 v20.8.14.4-lts,本版本没有引入新功能,而是集中修复了分布式查询连接数控制、Mutation 权限边界、ReplicatedMergeTree 并发操作、Poco SSL 竞态以及 Docker 启动脚本等一批在生产环境中真实影响稳定性的缺陷。本文以 docs/changelogs/archive/v20.8.15.11-lts.md 为骨架,结合当前仓库源码,逐条剖析每个修复背后的根因、涉及代码路径与升级验证方法,帮助使用 20.8 LTS 分支的团队理解本次升级的价值与风险。

版本背景:LTS 分支的补丁发布节奏

ClickHouse 的版本号采用MAJOR.MINOR.PATCH-build-<channel>形式。20.8是当时的 LTS 主版本,15为补丁序号,11为构建号,后缀-lts表明该构建属于长期支持通道,只包含经过精选的 Bugfix(通过 backport 机制从主分支移植回来)。这一点在变更日志的标题中体现得很直接:

### ClickHouse release v20.8.15.11-lts FIXME as compared to v20.8.14.4-lts

标题中残留的FIXME说明该文件是发布工程脚本自动生成的草稿标记,随后由维护者替换为正式差异描述。整个变更日志分为两个章节:Bug Fix(9 条精选修复)与NO CL ENTRY(1 条无变更记录的回滚项),没有New FeatureImprovementPerformance Improvement章节——这正是 LTS 补丁版的典型特征。

修复一:max_distributed_connections 并发控制修正

问题现象

变更日志首条修复(backport 自 [PR #17848])针对的是分布式查询的连接并发控制:

Fix max_distributed_connections (affectsprefer_localhost_replica=1andmax_threads!=max_distributed_connections).

在修复前,当同时满足两个条件时会出现连接数失控或不足:一是prefer_localhost_replica=1(默认开启,优先把查询派发给本机副本),二是max_threadsmax_distributed_connections不相等。

参数语义

在 src/Core/Settings.cpp 中,该参数的官方定义如下:

DECLARE(UInt64, max_distributed_connections, 1024, R"( The maximum number of simultaneous connections with remote servers for distributed processing of a single query to a single Distributed table. We recommend setting a value no less than the number of servers in the cluster. )", 0)

要点:

  • 默认值 1024,含义是"单条查询访问单个 Distributed 表时,与远端服务器建立的最大并发连接数";
  • 官方建议该值不小于集群中服务器的数量,否则分布式扇出会被连接数瓶颈卡住;
  • 该设置与max_threads是独立的:max_threads控制本机查询处理线程数,而本机线程中排除"从远端服务器拉取数据的线程",后者正是由max_distributed_connections约束(参见 src/Core/Settings.cpp 中max_threads的注释)。

底层机制:连接池按 min(max_distributed_connections, jobs_count) 收缩

在写入路径 src/Storages/Distributed/DistributedSink.cpp 中可以观察到该参数对线程池的实际约束:

size_t jobs_count = random_shard_insert ? 1 : (remote_jobs_count + local_jobs_count); size_t max_threads = std::min<size_t>(settings[Setting::max_distributed_connections], jobs_count); pool.emplace( CurrentMetrics::DistributedInsertThreads, CurrentMetrics::DistributedInsertThreadsActive, CurrentMetrics::DistributedInsertThreadsScheduled, max_threads, max_threads, jobs_count);

即连接线程池的实际规模是min(max_distributed_connections, 任务数)。当prefer_localhost_replica=1时,本机副本被优先选择,本地任务与远端任务的比例会改变jobs_count的构成;若max_threadsmax_distributed_connections不一致,旧逻辑在计算"还需要建立多少远端连接"时会出现偏差——这正是本次修复的核心。从当前源码结构看,修复后的逻辑统一以该设置为唯一上限对连接池做收缩,避免超额建连。

一个重要的后续:该 backport 被回滚

值得特别注意的是,变更日志的NO CL ENTRY章节记录了本次 backport 的命运:

NO CL ENTRY: 'Revert "Backport #17848 to 20.8: Fix max_distributed_connections"'([PR #21940],Maksim Kita)

也就是说,max_distributed_connections的修复虽然进入 20.8 分支,但随后又被回滚(通常意味着该修复在 20.8 的代码基线上引发了新的回归,或因依赖的其他修复未同步而无法独立生效)。这是 LTS 补丁发布中常见的"先试后撤"现象:升级到 v20.8.15.11-lts 的读者不应期待该连接数问题已被最终解决,而应结合后续版本(如 20.8.16 及以后的补丁)确认最终状态。

修复二:Mutation 操作的表引擎白名单

问题现象

Now mutations allowed only for table engines that support them (MergeTree family, Memory, MaterializedView). Other engines will report a more clear error.

修复前,对不支持 Mutation 的表引擎执行ALTER TABLE ... DELETE/UPDATE时,报错信息不明确甚至行为异常。修复后,Mutation 被明确限定在MergeTree 家族、Memory、MaterializedView三类引擎内,其他引擎会给出清晰错误。

源码证据:默认实现直接抛出 NOT_IMPLEMENTED

在 src/Storages/IStorage.cpp 中,存储引擎基类的默认mutate实现如下:

void IStorage::mutate(const MutationCommands &, ContextPtr) { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Mutations are not supported by storage {}", getName()); }

同理,killMutationwaitForMutationsetMutationCSN等配套接口在基类中全部默认抛NOT_IMPLEMENTED(见 src/Storages/IStorage.cpp)。这构成了白名单机制的骨架:只有覆写了这些接口的引擎(MergeTree 家族及其派生、Memory、MaterializedView)才能执行 Mutation,其余引擎直接落入基类的清晰报错路径。

这类报错在仓库中还有类似形态,例如 MergeTree 对不可变磁盘的防护(src/Storages/MergeTree/MergeTreeData.cpp):"Mutations are not supported for immutable disk '{}'"——说明引擎层对"是否允许 Mutation"有系统性的多级校验。

修复三:ALTER DELETE 自引用谓词的死锁

Fix a deadlock inALTER DELETEmutations for non replicated MergeTree table engines when the predicate contains the table itself.

触发场景:对非复制型 MergeTree(非 Replicated)表执行ALTER TABLE t DELETE WHERE ...,且删除谓词(predicate)中引用了表自身(例如子查询WHERE id IN (SELECT id FROM t WHERE ...))。修复前会导致 Mutation 任务死锁(对应 issue #20558)。

根因推断:从源码结构看,非复制 MergeTree 的 Mutation 依赖在表数据上获取锁与执行DELETE子查询两个动作。当谓词引用表自身时,执行子查询需要读取同一张表,而该表又处于"正在被 Mutation 修改"的锁定状态,两者互相等待即形成死锁。修复([PR #21477])通过调整谓词求值阶段对表自身的访问方式/锁顺序打破了这个循环等待。

实战建议:如果业务中需要基于"表自身内容"做删除,优先把筛选结果物化到临时表或先SELECT出 ID 列表再分步执行,避免单条ALTER DELETE的自引用子查询。

修复四:ReplicatedMergeTree 的 OPTIMIZE/ALTER 等待逻辑

Fix waiting forOPTIMIZEandALTERqueries forReplicatedMergeTreetable engines. Now the query will not hang when the table was detached or restarted.

修复前,对 ReplicatedMergeTree 执行OPTIMIZE TABLEALTER TABLE并等待完成时,如果操作过程中表被detach(卸载)或重启,查询会无限期挂起。修复([PR #22118])让等待逻辑能感知表生命周期变化并及时返回,不再"卡死"。

这类等待语义在运维脚本中非常常见(例如发布流程里执行OPTIMIZE ... FINAL后等待其完成),因此该修复直接提升了自动化运维的健壮性:表被摘除或实例重启后,等待中的查询应快速失败而非挂起

修复五:ReplicatedMerge 表的 Decimal 列 MODIFY COLUMN

Fix bug for ReplicatedMerge table engines whenALTER MODIFY COLUMNquery doesn't change the type of decimal column if its size (32 bit or 64 bit) doesn't change.

触发场景:对 ReplicatedMerge 家族表执行ALTER TABLE t MODIFY COLUMN c Decimal(18, 2),将列从一种 Decimal 类型改为另一种同字节宽度的 Decimal 类型(例如 32 位宽度内的精度调整,或 64 位宽度内的精度调整),修复前元数据变更不会真正生效。

根因推断:从实现细节看,Decimal 类型的物理宽度由总位数决定(Decimal32/Decimal64/Decimal128),而精度P与标度S的变化未必改变底层存储大小。旧逻辑在判断"类型是否变化"时按存储宽度比较,导致同宽度的 Decimal 精度/标度调整被误判为"无变化"而跳过 ALTER 记录,最终副本间元数据不一致。修复([PR #21728])在类型等价性判断中纳入了完整的 Decimal 参数(precision/scale)。

验证方法:升级后可执行:

CREATE TABLE t (d Decimal(10, 2)) ENGINE = ReplicatedMergeTree('/clickhouse/tables/t', 'r1') ORDER BY tuple(); ALTER TABLE t MODIFY COLUMN d Decimal(10, 4); SHOW CREATE TABLE t; -- 期望显示 Decimal(10, 4)

观察SHOW CREATE TABLE输出中该列的精度/标度是否同步更新。

修复六:不再对已覆盖数据分片执行 Mutation

Now clickhouse will not throwLOGICAL_ERRORexception when we try to mutate the already covered part.

在分区合并(merge)或 Mutation 迭代过程中,某个数据分片(part)可能已经被后续 Mutation 覆盖或并入更大的 part。旧逻辑再次尝试对其执行 Mutation 时会抛出LOGICAL_ERROR内部错误(issue #22013),导致 Mutation 任务异常中断。修复([PR #22291])让执行器跳过已覆盖的分片,保证任务顺利完成。

背景说明LOGICAL_ERROR是 ClickHouse 中表示"内部状态不一致"的错误级别,正常运行时不应出现。该修复消除了此类虚假错误,让 Mutation 在执行期间发生并发 merge 时依然可收敛。

修复七:Join 常量列物化问题

Join tries to materialize const columns, but our code waits for them in other places.

当 JOIN 操作试图物化(materialize)常量列(const columns)时,代码的其他部分仍在按"常量列"的形态等待处理,导致 JOIN 结果行为异常。修复([PR #18982])统一了 JOIN 流程中对常量列物化与消费的时序。

这类问题在"常量与普通列混合参与 JOIN 键/投影"的场景下出现,例如SELECT ... FROM a JOIN b ON a.k = 1或使用constant表达式作为 JOIN 条件的查询。

修复八:Poco SecureSocket 的 SSL 对象竞态

Fixed race on SSL object inside SecureSocket in Poco.

该修复位于 ClickHouse 内置的 Poco 网络库(base/poco 目录下的 Foundation/Net/NetSSL_OpenSSL 等模块)中。SecureSocket 在多线程并发读写时对底层 SSL 对象存在竞态,可能引发连接异常或崩溃。修复([PR #21456])通过为 SSL 对象增加同步保护消除了该竞态。

影响面:所有使用 TLS/SSL 的网络通道——包括 HTTPS 端口上的 Native/HTTP 协议连接、与外部系统的 TLS 交互等。在多线程高并发访问 HTTPS 端口的场景中尤其值得关注。

修复九:Docker entrypoint 在 LOG_PATH 为空时避免 chown "."

Docker entrypoint: avoid chown of.in case whenLOG_PATHis empty.

修复([PR #22102])针对 Docker 启动脚本 docker/server 目录下的 entrypoint 逻辑:当配置中未设置LOG_PATH(日志路径为空)时,旧脚本会对当前目录.执行chown,可能导致文件属主被意外修改或启动失败。修复后仅在LOG_PATH非空时才执行目录属主调整。

部署影响:使用 Docker 镜像并自定义了不含LOG_PATH的配置的部署方式,升级后可避免容器启动时的属主异常。

变更日志中的元信息与后续跟进建议

NO CL ENTRY 章节的阅读方式

NO CL ENTRY表示该 PR 没有附带的变更日志条目(changelog entry),通常是回滚类提交或纯内部提交。本版本中的回滚对象正是第一条max_distributed_connections修复,建议升级前确认:

-- 查看当前版本 SELECT version();

并对照官方发布说明,确认后续补丁版本(20.8.16+)中该修复是否被重新引入及最终形态。

升级验证清单

结合本次 9 条修复,建议在生产环境升级后依次验证:

验证点对应修复验证方式
分布式查询连接数max_distributed_connections设置max_distributed_connectionsmax_threads为不同值,观察system.processes与日志中的远端连接数
Mutation 白名单引擎约束LogTinyLog等引擎执行ALTER TABLE ... DELETE,确认收到清晰的 NOT_IMPLEMENTED 错误而非内部异常
自引用谓词删除死锁修复执行谓词引用表自身的ALTER DELETE,确认可正常完成而非挂起
Replicated 表等待等待逻辑OPTIMIZE TABLE ... FINAL等待期间 detach 表,确认查询及时返回而非挂起
Decimal 类型变更MODIFY COLUMN同宽度 Decimal 精度调整后执行SHOW CREATE TABLE核对
SSL 并发Poco 竞态对 HTTPS 端口执行高并发压力测试,观察连接稳定性

总结

v20.8.15.11-lts 是一个典型的 LTS 补丁版本:没有新功能,全部精力用于消除 20.8 分支上的稳定性缺陷。其中Mutation 白名单化ReplicatedMerge 等待逻辑的修复直接改善了运维可观测性与自动化脚本的健壮性;Decimal 列 MODIFY COLUMN修复避免了副本间元数据漂移;而max_distributed_connections修复的"进入后又回滚"则提醒我们:LTS 分支的每个 backport 都需要在目标基线上独立验证,变更日志中的NO CL ENTRY章节同样值得仔细阅读。对于仍在 20.8 LTS 分支上的团队,建议在升级到本版本后按上文清单做一轮针对性的回归验证,并持续跟踪后续补丁对回滚项的最终处理。

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

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询