1. ClickHouse Schema修改引发的Merge阻塞问题深度解析作为一名长期与ClickHouse打交道的工程师我最近遇到一个由Schema修改引发的Merge阻塞问题整个过程堪称教科书级的分布式系统故障排查案例。本文将详细记录从问题发现到最终解决的完整过程并深入分析背后的技术原理。1.1 问题背景与现象我们在生产环境中对emo_mdm_flowwork_pt1m_local表执行了添加5个String类型列的DDL操作ALTER TABLE default.emo_mdm_flowwork_pt1m_local ON CLUSTER cluster ADD COLUMN IF NOT EXISTS host String DEFAULT AFTER connType, ADD COLUMN IF NOT EXISTS path String DEFAULT AFTER host, ADD COLUMN IF NOT EXISTS referrer String DEFAULT AFTER path, ADD COLUMN IF NOT EXISTS title String DEFAULT AFTER referrer, ADD COLUMN IF NOT EXISTS referrerHost String DEFAULT AFTER title;操作看似简单却引发了连锁反应Mutation阻塞数据删除操作在部分分片上永久阻塞Merge失败后台Merge任务大量失败错误日志显示类型转换异常查询异常涉及新增列的查询会触发相同错误1.2 初步排查与应急处理1.2.1 资源池调整无效我们首先怀疑是MergeAndMutatePool资源不足导致。将background_pool_size从32调整到48后# 监控指标变化 ClickHouseMetrics_BackgroundMergesAndMutationsPoolSize: 64 → 96虽然部分分片恢复但仍有分片持续阻塞说明不是根本原因。1.2.2 错误日志分析关键错误信息反复出现Bad cast from type DB::ColumnNothing to DB::ColumnLowCardinality while reading from part /path/to/part堆栈显示问题发生在VerticalMergeStage且与复杂类型处理相关。特别值得注意的是报错的不是新增的5个String列问题只出现在添加列之前创建的part中涉及LowCardinality类型的复合列Map/Array/Tuple2. 深入诊断过程2.1 问题复现条件验证通过以下查询验证问题触发条件-- 不包含新增列的查询 → 正常 SELECT count(1) FROM table WHERE timestampMs BETWEEN... AND flowId IN(...) -- 包含新增列的查询 → 报错 SELECT count(1) FROM table WHERE timestampMs BETWEEN... AND referrer value2.2 Part元数据分析对比新旧part的列存储情况# 新part包含新增列 ls /path/to/new_part | grep referrer referrer.bin referrer.cmrk2 ... # 旧part缺少新增列 ls /path/to/old_part | grep referrer [无输出]2.3 系统表查询通过系统表定位受影响范围-- 查找所有报错的part SELECT database, table, partition_id FROM system.parts WHERE name IN ( SELECT DISTINCT result_part_name FROM system.merges WHERE last_exception LIKE %Bad cast% )结果显示问题集中在emo_mdm_flowwork_*系列表且只影响添加列前创建的part。3. 根本原因分析3.1 ClickHouse的Merge机制当执行Merge操作时ClickHouse会确定Merge算法Horizontal/Vertical读取源part数据处理缺失列新增列场景写入新part在Vertical Merge模式下列是按需读取的这触发了缺失列的默认值填充逻辑。3.2 默认值填充的两阶段过程当读取的part缺少表中存在的列时第一次补列fillMissingColumns()为简单类型填充类型默认值为复合类型准备offset信息// 示例为Map类型准备默认数据结构 ColumnPtr column data_type-createColumn(); column-insertDefault();第二次补列evaluateMissingDefaults()计算表达式默认值此处发生了ColumnNothing到ColumnLowCardinality的错误转换3.3 复合类型的Substream处理复杂类型的列如Map(LowCardinality(String), String)通过Substream机制处理SubstreamPath标识嵌套结构中的具体子列Serialization各类型实现自己的序列化逻辑SerializationMapSerializationArraySerializationTuple当处理缺失的复合列时类型系统未能正确处理LowCardinality子列的默认值构造。4. 解决方案与验证4.1 临时解决方案强制使用Horizontal Merge算法SET vertical_merge_algorithm_min_rows_to_activate 2000000000; SET vertical_merge_algorithm_min_bytes_to_activate 536870912000;然后执行OPTIMIZEOPTIMIZE TABLE emo_mdm_flowwork_pt1m_local PARTITION 20250925 FINAL;4.2 根本解决通过代码分析发现问题根源缺失列处理时未正确维护LowCardinality类型信息复合类型的Substream路径处理存在边界条件缺陷添加调试日志确认问题LOG_DEBUG(log, Missing column: {}, type: {}, column.name, column.type-getName());最终通过以下方式彻底解决升级到修复该问题的ClickHouse版本对历史part执行强制重写5. 经验总结与最佳实践5.1 问题处理流程优先恢复服务通过调整Merge策略快速止血保留现场在重启前收集堆栈、日志和指标根因分析通过代码走查和日志验证假设彻底修复升级或应用补丁5.2 ClickHouse运维建议Schema变更时避免在生产高峰执行提前评估影响范围考虑使用ALTER TABLE ... MODIFY SETTING调整Merge策略监控关键指标SELECT * FROM system.mutations WHERE is_done 0; SELECT * FROM system.replication_queue WHERE last_exception ! ;复杂类型使用谨慎使用多层嵌套的LowCardinality为复杂类型显式指定默认值5.3 深度技术理解通过这次事故我们深入理解了Merge算法选择逻辑if (sum_rows settings.vertical_merge_algorithm_min_rows sum_bytes settings.vertical_merge_algorithm_min_bytes) return MergeAlgorithm::Vertical;Substream处理流程通过ISerialization::enumerateStreams()收集子列每个子列对应独立的存储文件类型系统转换ColumnNothing作为占位符需要显式处理到目标类型的转换这次问题的解决过程再次证明在分布式系统中表面简单的操作如加列可能引发深层次的系统交互问题。关键在于建立系统的全局视角并掌握从现象到本质的分析方法。
企业数字化 ERP 产品动态
相关推荐
解决frp内网穿透认证失败(1008)的完整指南 1. 问题现象与初步分析最近在MacBook上配置frp内网穿透服务时遇到了一个典型错误:当尝试通过公网服务器访问本地服务时,连接被中断并返回错误信息"disconnected (1008): unauthorized"。这个错误表明客户端与服务器之间的认证过程出现了问题。… · 2026/11/3 7:02:52
用version字段解决并发重复更新:乐观锁原理与实战 并发场景下的重复更新问题,几乎是每个做业务系统的开发者迟早要撞上的一堵墙。定时任务跑重了、消息队列投递了两次、用户手抖连点了两下提交按钮、前端重试机制把同一个请求又发了一遍——这些情况单独拎出来都不算大问题,但它们共同指向同一个后果&… · 2026/11/3 7:02:52
CommonDialog保存与另存为:属性配置、覆盖提示与避坑指南 翻老项目代码的时候,只要看到窗体的通用对话框控件,十有八九就是那一行CommonDialog1.ShowSave。这个调用看着简单到不能再简单,弹个框、拿到一个路径、然后写文件,三步走完。但就是这三步,我见过太多次翻车࿱… · 2026/11/3 7:01:52
大模型选型与Agent开发实战:从模型清单到选型地图的完整指南 1. 从"模型清单"到"选型地图":这份盘点到底解决什么问题每隔一段时间,圈子里就会冒出一份"知名大模型盘点"。大多数人扫一眼就划走了,因为清单本身没有价值——真正有价值的是:当你要动手做一个 Ag… · 2026/11/3 7:39:59
Vue 3实现前端邮件预览组件:解析EML文件与移动端适配 1. 项目概述在Web开发中,邮件处理一直是个颇具挑战性的领域。传统的邮件系统通常需要用户下载.eml文件后,再通过本地邮件客户端打开查看。这种体验不仅繁琐,而且对于移动端用户极不友好。今天我要分享的,是一个基于Vue 3和Vant UI… · 2026/11/3 7:39:59
Java boolean占几个字节?从JVM规范到HotSpot实现的四层解析 1. 一个被问了无数遍却总有人答错的问题“Java里boolean到底占几个字节?”——这问题我在面试间里听过不下二十次,在技术群里见过无数次争论,甚至有一次团队内部做代码评审,两个同事为了它差点拍桌子。有人说1个字节,有… · 2026/11/3 7:39:59
实测小米mimo-v2.6-flash代码能力:轻量模型本地部署实战评测 实测 mimo-v2.6-flash 的写代码能力,邀请码放文末最近圈子里的朋友都在讨论小米开源的 mimo-v2.6-flash,说是面向代码生成场景做了专门的轻量化优化,模型体积不大但效果很能打。我这个人对"轻量模型挑战大模型"这种事向来有执念&am… · 2026/11/3 7:39:59
智能微电网多目标优化调度实战解析 1. 项目背景与核心价值去年夏天参与某工业园区微电网改造时,我亲历了这样一个场景:光伏阵列在正午发着过剩的电力,而储能系统却因调度策略保守不敢充满,柴油发电机却因合同电量要求不得不保持最低运行负荷。这种能源"三输&qu… · 2026/11/3 7:39:59
Chat2Workflow深度评测:15款模型生成ComfyUI工作流能力横评 Chat2Workflow这个方向,最近在AI工作流圈子里热度一直在涨。说白了,需求非常直接:你用自然语言描述一句“把这张人像照片修成老照片质感,再放大两倍”,它直接给你生成一份能加载进ComfyUI的工作流文件。想法很美好&… · 2026/11/3 7:38:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46