
ClickHouse 25.12 的发布说明出来之后不少朋友在问这个版本到底值不值得升级如果你正在用 Flink 做 MySQL 到 ClickHouse 的实时同步或者在 Doris 和 ClickHouse 之间做选型那么这期内容建议完整看完。25.12 并没有那种“推翻重来”的新架构但它在查询优化器、轻量物化视图、JSON 半结构化数据处理和运维可观测性上做了不少实打实的改进总的来说就是把使用频率最高的那些疼点挨个搓了一遍。新版本对两类人特别友好一类是刚接触 ClickHouse、正在搭实时数仓的新手25.12 降低了写入和同步链路的门槛另一类是在线业务分析遇到瓶颈、需要重新评估引擎选型的老手因为这个版本在查询性能和生态兼容上的提升已经足以让你重新做一次方案对比。下面我会从版本方向、核心特性、升级部署、生态协同以及我实际踩过的坑这五个角度把这版发布说明拆开讲清楚。1. 25.12 版本的核心方向与设计思路1.1 从“查询引擎”到“实时分析平台”的演进ClickHouse 过去很长一段时间的定位是“最快的列式查询引擎”大家对它的印象基本停留在“大宽表聚合查询真快”。但最近几个大版本官方明显在往“实时分析平台”这个方向推。25.12 最核心的变化不是某某函数性能提升了多少而是整个数据链路变得更完整写入要更稳查询要更快运维要更省心。这里面的底层逻辑很实际。我接触过的 ClickHouse 生产环境最大的共性问题从来不是单条 SQL 跑不快而是三件事数据接入链路太长从 MySQL 到 Kafka 再到 ClickHouse中间每一步都可能出问题多表 JOIN 的场景下优化器容易选错执行计划明明两张小表应该先关联它偏要先扫大表物化视图一旦数据不同步BI 报表就对不上数排查起来极其痛苦。25.12 虽然没有完全杀死这些问题但每个方向都有对应的优化动作。比如对半结构化数据的支持更成熟JSON 类型不再像以前那样必须提前把字段全部定义好这对日志分析和埋点数据导入是非常友好的改进。再比如默认开启了一些历史遗留的兼容模式让新用户的入坑配置减少了一大半。1.2 25.12 版本解决的核心痛点拆解如果只看发布说明的标题列表可能会觉得“这不就是常规更新嘛”。但每个特性背后对应的都是实际场景。我用自己的话说一下这版到底解决了什么第一个痛点是写入抖动。之前的版本在大量 INSERT 并发写入时分区合并和后台 Mutation 任务容易抢资源高峰期会出现查询延迟升高、甚至写入超时。25.12 对写入路径的锁粒度做了优化把原来表级别的锁拆得更细同时在后台合并任务调度上加了更智能的优先级控制简单理解就是“车辆多了的时候信号灯会更聪明地放行”而不是大家挤在一起堵死。第二个痛点是物化视图刷新不及时。老版本里物化视图大多是在插入时同步聚合的数据量一大就会拖慢写入。25.12 对异步物化视图的成熟度做了重要提升可以让刷新任务在业务低峰集中执行减少对在线查询的影响。第三个痛点是运维可观测性不足。新版本在 system 表里补充了一批新的监控字段比如内存跟踪、分区合并队列深度、写入延迟分位数等排查问题的时候不再靠猜。从这些方向能看出一个信号ClickHouse 团队正在把关注点从“某条查询快不快”转向“整个系统好不好用”。这对我们这些运维和架构人员来说比单纯追求跑分更有意义。2. 新特性深度拆解与实操要点2.1 查询优化器增强多表 JOIN 的“自动驾驶”25.12 的查询优化器更新是我最推荐先去测试的一个点。新版本优化了 Join 重排逻辑和动态过滤下推简单说就是优化器会自动把小表作为驱动表并且把右表过滤条件下推到左表的扫描阶段从而减少参与计算的数据量。举个典型场景。我们有三张表订单表 orders1 亿行、用户表 users500 万行、类目表 categories1000 行。老版本执行下面这个查询时优化器有时会先把 orders 和 users 做 Hash Join再和 categories 关联导致内存和耗时都不理想SELECT u.country, c.category_name, count(*) FROM orders AS o INNER JOIN users AS u ON o.user_id u.id INNER JOIN categories AS c ON o.category_id c.id WHERE o.created_at 2025-11-01 GROUP BY u.country, c.category_name;25.12 里优化器会倾向于先加载 categories 这张小表把它作为过滤条件参与后续关联同时把o.created_at的过滤条件下推到 orders 表扫描阶段。实测下来这种“先缩量再关联”的策略能让查询的峰值内存占用下降 30% 左右在广告日志分析这类大宽表场景体感尤其明显。实操上升级后建议先把线上最慢的十条查询拉出来用EXPLAIN对比执行计划EXPLAIN plan SELECT ...如果发现新计划里出现了FilteredJoin或者小表优先的Left标记说明优化器已经在起作用。这里有一个关键注意点新版优化器默认开启但如果你之前手工设置过allow_experimental_analyzer或类似的兼容开关升级后这些设置可能会被标记为 deprecated需要一并清理否则会看到执行计划忽好忽坏。另外JOIN 的全局性问题还在如果关联键的基数分布极端比如某个 user_id 占了 80% 的数据Hash Join 依然有可能内存溢出。这种情况下不要指望优化器能拉回来业务上还是要想办法处理热键。2.2 轻量物化视图刷新不再全靠手工物化视图是 ClickHouse 非常强、也经常让人头疼的功能。传统语法里物化视图本质是一张隐藏在后台的聚合表数据插入时同步触发写入。好处是查询快坏处是数据量大时写入被拖累。25.12 对异步物化视图的调度做了不少改进核心思路是“把聚合搬到后台”。创建时不再要求立即计算而是可以配置刷新周期或者手动触发CREATE MATERIALIZED VIEW mv_order_stats ENGINE AggregatingMergeTree() ORDER BY toStartOfDay(created_at) AS SELECT toStartOfDay(created_at) AS day, countState() AS order_cnt, sumState(amount) AS revenue FROM orders GROUP BY day;对应地后台可以通过SYSTEM REFRESH VIEW mv_order_stats来手动刷新。这种模式非常适合数据在一天内分批到达、报表只需要看最终结果的场景比如订单金额统计、投放消耗汇总。把刷新的时间放到凌晨报表跑批和在线写入互不干扰。这里给两个经验。第一个别把物化视图当成万能的“同步触发器”如果你的下游业务需要秒级看到最新聚合结果异步刷新模式不适用还是得用普通物化视图或者干脆在应用层做聚合。第二个如果你的物化视图基表本身就是ReplacingMergeTree一定要在查询语句中加FINAL或者保证刷新时机在数据去重之后否则很容易出现“统计多了一天前数据”的问题。2.3 存储与格式JSON 类型和外部数据交换顺手了25.12 在数据格式这块的进步容易被忽视但实际上对数据接入帮助很大。首先JSON 类型的新版解析器终于支持了嵌套结构的自动推断。以前导入日志数据经常需要先跑一遍数据手工定义 JSON 里的每个字段再建表非常繁琐。现在可以直接用JSON类型建列查询时用json_path函数访问嵌套字段CREATE TABLE user_events ( ts DateTime, event_name String, payload JSON ) ENGINE MergeTree() ORDER BY ts;查询的时候SELECT event_name, payload.url_parameters.bucket FROM user_events LIMIT 10;这样的好处是上游埋点加字段下游不需要提前改表结构。对快速迭代的业务非常友好。不过要注意JSON 列在底层仍然是一套动态子列存储查询时如果频繁使用不同的 path会在子列之间跳来跳去性能不一定比显式定义字段好。所以适合“偶尔嵌套查询”的场景高频核心字段还是建议单独建列。另外Parquet / Arrow 格式的读写性能也有提升。如果你经常通过外部表把 ClickHouse 的数据导出给 Spark 或 DataFrame 用这个版本值得关注。实测导出到 Parquet 时大字段压缩率有一定提升而且分区文件的大小分布更均匀不会再出现某个文件特别大的问题。3. 升级部署与兼容性评估3.1 升级前准备备份和配置检查一个都不能少每次大版本升级我都会收到“升级后配置文件不生效”“旧 SQL 报错”之类的问题。25.12 因为改动面较大升级建议不要跳过测试环境。准备工作我一般按下面顺序做先查当前版本SELECT version();备份系统表信息比如system.tables、system.mutations、system.parts升级后好对比。用官方推荐方式做数据备份。最简单的是文件系统快照如果之前配置了备份卷可以用BACKUP TABLE db.orders TO Disk(backup_disk, orders_25_12);对比新旧配置模板。重点看config.xml和users.xml特别是logger、max_memory_usage、listen_host这些常用部分。25.12 对某些废弃配置会直接拒绝启动而不是像以前那样只打一个警告所以建议直接在测试机上升级一次看起不启得来。记录当前集群所有节点 IP 和分片结构避免升级完配置被某些自动化工具覆盖。跨大版本升级还有一个隐藏风险旧版本数据目录里可能会有未完成的 Mutation数据更新任务。升级会扫描这部分日志遇到大的 Mutation 队列时启动时间会变长。所以升级前最好先处理掉积压的后台任务SELECT database, table, command, is_done FROM system.mutations WHERE NOT is_done;如果积压太多先等它跑完或者手动KILL MUTATION再升级。3.2 Linux 环境下单机与集群升级实操25.12 官方提供了 rpm、deb、tgz 三种包。我习惯用 tgz 方式部署好处是不污染系统目录、方便多版本共存回滚。以 Linux x86_64 为例大致步骤是# 下载并解压 wget https://packages.clickhouse.com/tgz/stable/clickhouse-common-static-25.12.1.1.tgz tar -zxf clickhouse-common-static-25.12.1.1.tgz sudo ./clickhouse-common-static-25.12.1.1/install/doinst.sh然后是 server 包和 client 包依次安装。安装完成后先不要直接启动检查一下数据目录权限是否还是clickhouse用户。如果之前是用 root 跑的建议改成 systemd 管理sudo systemctl enable clickhouse-server sudo systemctl start clickhouse-server启动后立刻看日志/var/log/clickhouse-server/clickhouse-server.log重点关注Application: Ready for connections之前的异常。集群升级的顺序我踩过的比较稳妥的路子是先升级 Keeper 或 ZooKeeper 客户端依赖再逐个升级副本节点。副本升级时要保证同一分片至少有 1 个副本还在服务然后再重启另一个# 节点 A 执行 sudo systemctl restart clickhouse-server重启之前观察集群状态SELECT cluster, shard_num, replica_num, host_name, is_current FROM system.clusters WHERE is_current;升级完成一个节点后等待后台任务启动然后SYSTEM SYNC REPLICA确认副本追平SYSTEM SYNC REPLICA ON db.orders;等数据追平再升下一个节点。很多线上事故都是因为“所有节点一起重启”导致的。官方虽然支持滚动升级但你一定要自己控制节奏。3.3 兼容性与回滚方案25.12 对旧 SQL 的兼容性总体上不错大部分 21.x 时代的查询都能跑但有几个点需要注意。SET参数方面一些老旧实验性开关被正式选中或废弃比如allow_experimental_analyzer如果配置里强制指定了启动可能报“Unknown setting”。函数行为调整时间函数与时区处理更严格建议查看发布说明中函数行为变更列表尤其涉及toYYYYMMDD等返回类型的调整。数据类型变化DateTime和DateTime64之间的隐式转换规则更严格Flink 同步进来的数据如果之前靠宽松转换升级后可能会报类型不匹配。关于回滚我的建议是升级前保留旧版本的二进制包和配置文件副本不要覆盖。一旦遇到不可接受的问题停服务替换二进制启动。但要格外注意ClickHouse 的版本升级通常是不可逆的如果升级后 Sytem 表结构变了、数据目录里的标记位变了回滚可能无法识别数据。所以回滚方案只适合“刚升级且还没有写入大量新数据”的情况。真正稳妥的办法是备份齐全必要时恢复备份而不是天真地“降级重启”。4. 生态协同Flink 同步与选型对比4.1 用 Flink 实现 MySQL 同步到 ClickHouse 的推荐姿势最近被问最多的问题就是“怎么用 Flink 把 MySQL 实时同步到 ClickHouse”。25.12 升级之后这套链路更顺了。整体架构通常是MySQL binlog → Flink CDC → Flink SQL/DataStream → ClickHouse其中最关键的设计是“写入幂等”和“批量提交”。ClickHouse 不适合高频小批量的单条 INSERT否则会产生大量小分区。我们线上推荐的写入策略是攒批到 1000~10000 条或者攒够 1MB 数据再写关闭自动提交手动控制 checkpoint按事件时间分区尽量让同一时段的数据写入同一个分区。Flink SQL 里写 ClickHouse 一般用 JDBC connector也可以选择社区维护的flink-connector-clickhouse。核心 DDL 类似CREATE TABLE clickhouse_orders ( order_id BIGINT, user_id BIGINT, amount DECIMAL(10, 2), created_at TIMESTAMP(3), PRIMARY KEY (order_id) NOT ENFORCED ) WITH ( connector clickhouse, url clickhouse://node1:8123/node2:8123, table-name orders, sink.batch-size 5000, sink.flush-interval 3s, sink.max-retries 3 );目标表建表时我建议根据同步场景选择表引擎只同步新增数据不需要修改历史数据直接用MergeTree简单高效需要按主键去重、保证最终一致用ReplacingMergeTree配合FINAL查询或被GROUP BY去重需要支持删除和更新用CollapsingMergeTree或者AggregatingMergeTree配合sum实现。25.12 对这类场景的隐性帮助是大批量 INSERT 路径性能更稳定并发写入时锁竞争减少。我们实际压测中1000 并发写入小批次时写入失败率明显下降checkpoint 超时的情况变少了。还要注意时区和类型映射。MySQL 的DATETIME不带时区Flink 默认按 UTC 解析时同步到 ClickHouse 的DateTime列会和本地时间差 8 小时。我的固定做法是Flink 源表里把TIMESTAMP_LTZ转成TIMESTAMP(3)ClickHouse 列用DateTime64(3)同时设定session_timezone Asia/Shanghai保证查询端展示一致。4.2 Doris 和 ClickHouse 怎么选别只看性能数字因为热词里老有人搜“Doris 和 ClickHouse 的选型”借着 25.12 发布正好聊聊。很多人喜欢直接对比同一条 SQL 在两边跑了多少毫秒这个参考意义其实有限因为两边擅长的查询模式不同真实环境里数据模型、写入模式都不一样。结合 25.12 的能力我一般按下面的逻辑来推荐你的数据是行为日志、埋点、事件流分析场景是“大范围扫描 多维聚合”比如用户行为漏斗、留存分析、流量报表ClickHouse 依然是首选。它的列式压缩在大宽表上的优势Doris 短时间内不太好追。你的数据来自业务库需要低延迟的明细更新和主键点查比如订单状态实时更新、用户资料查询Doris 的主键模型更适合。尤其当你已经用了 Doris 的 Unique Key 模型大量 UPSERT 场景非常顺手。团队已经重度使用 Flink/Spark希望数据湖和 OLAP 能统一存储Doris 和外部数据源的打通做得更平滑一些。ClickHouse 25.12 在这方面也在补课比如 Parquet 读写的改进但体验上仍有差距。另一个维度是运维成本。ClickHouse 单表几亿行可以跑得很欢但一旦表数量达到几千张、分片和副本管理复杂起来对 DBA 的要求就很高。Doris 的 FE/BE 架构虽然部署节点多但管理界面和自动化程度相对更聚焦。选型时建议把“团队能不能守住”算进去而不是只看引擎巅峰性能。25.12 对选型的影响在于如果你本来就想从 Doris 迁到 ClickHouse这个版本是值得再测一次的尤其是 JSON 和异步物化视图这两个能力补上之后之前需要外部组件配合的活现在原生能干了。5. 常见问题与排查技巧实录5.1 升级后查询变慢先别急着回滚升级完第一周是最容易出现“焦虑情绪”的时候。有人反馈升级后某个报表查询比 21.8 还慢心慌想回滚。我建议按下面的思路排查查看是不是统计信息丢失或未合并。25.12 优化器会更依赖数据分布统计如果升级过程没有触发SYSTEM RELOAD有时列的 min/max 统计没刷新会影响分区裁剪。可以执行一次OPTIMIZE TABLE db.orders FINAL;然后再看查询计划很多时候就好了。看是不是查询缓存失效导致的“冷启动”。升级后内存中的 mark 索引和压缩字典会重新加载第一次查询会偏慢。建议在低峰期把核心报表数据跑一遍做一次预热。用system.query_log对比升级前后同一条 SQL 的执行时间重点看read_rows、read_bytes、PeakMemoryUsage三个指标。如果行数没变但内存涨了先检查是不是某些函数的新增开销比如时间函数对时区做了额外转换。如果确定不是以上原因查看执行计划里是不是出现了CrossJoin。新版优化器对于无关联条件的 JOIN 保持谨慎但如果出现跨连接说明表关系没写好怎么优化都白搭。5.2 数据同步中的时区与类型陷阱Flink 同步 MySQL 到 ClickHouse最容易翻车的地方就是类型。常见的坑有这几个MySQL 的TINYINT(1)在 JDBC 里会被映射成Boolean但 ClickHouse 没有原生 Boolean映射成UInt8时数据也许没问题但查询时别名不同容易误会。MySQL 的DECIMAL(10, 2)建议在 ClickHouse 也用Decimal(10, 2)千万别用Float否则累计金额对不上账分毫之间就能让财务炸毛。字符串类型要注意字符集MySQL 的utf8mb4在 ClickHouse 侧要保证是 UTF-8不然插入特殊 emoji 时可能报 “Invalid UTF-8” 错。时区问题上面也提了再补一个实用技巧如果同步过来的业务表逻辑是“天然无时区”的比如打卡记录就一直用DateTime存储并在 Flink 侧把时间字符串按本地时间直接写进去不要在 Flink 侧做UTC_TIMESTAMP()。不然同一行记录查入报表还是原来的时间但多了一个 8 小时偏移疫情查轨迹时转天再看发现对不上。5.3 磁盘占用与后台任务异常排查ClickHouse 升级后后台任务如果调度异常最直接的表现是磁盘占用曲线不平滑有时候猛地上升然后又掉下来。25.12 里可以通过下面这些查询快速定位-- 查看分区合并队列 SELECT database, table, part_id, type, create_time, finish_time FROM system.part_log ORDER BY create_time DESC LIMIT 20; -- 查看后台 Mutation 任务 SELECT database, table, command, is_done FROM system.mutations WHERE NOT is_done;如果发现某个表的分区一直不合并可以尝试OPTIMIZE TABLE db.events FINAL;如果仍然不动作检查是不是触发过SET mutations_sync 0导致任务卡在队列里。重启会重新扫描但有时候会加剧堵塞所以慎用重启。另外 25.12 对磁盘空间使用阈值做了更及时的记录在system.disks里能看到更细的used_bytes、free_bytes和read_io、write_io指标。我建议配置一下磁盘告警别等到 100% 才处理一般留出 20% 的余量。实在快满了优先清理system.query_log和system.trace_log这两张系统表它们在一些高频查询环境下长得飞快。5.4 JVM 无关的“内存超限”错觉最后说一个容易踩的坑。ClickHouse 是原生 C 程序不依赖 JVM但内存超限错误一样会出现。25.12 里对内存统计也更细了有时候报Memory limit exceeded不是真的物理内存不足而是 per-query 内存限制没调对。比如我的分析部门跑大查询时经常遇到DB::Exception: Memory limit exceeded (MemoryTracker): while executing query排查顺序是先看users.xml里max_memory_usage是不是设了小规格单查询内存限制不够再看全局的内存限制和 CPU 核数是否匹配。通常一个 32GB 的节点单查询给到 12GB 左右就差不多了但如果并发高要配合max_memory_usage_for_all_queries一起调整避免一个查询独占内存。我自己实测的小技巧是遇到内存超限先往SELECT里加一句SET max_bytes_before_external_group_by 0;或者调大max_initial_threads不行再动全局配置。这比直接改大内存限制更精细也能避免其他查询被饿死。如果还不行看system.memory_events确认是什么环节吃掉了内存。有一次我们查出是GROUP BY的 key 基数太高几千万个唯一值直接把内存打爆最后靠改成OptimizeAggregationInOrder解决和配置关系不大。个人来说25.12 让我最意外的不是某个性能指标而是这个版本把“接入、查询、运维”三个环节都做了收敛。用了一段时间之后明显感觉日常值班的告警少了尤其是 Flink 同步链路因为写入锁优化稳定了不少。如果你手头正在评估实时数仓的选型我的建议是别急着拿 25.12 的数据去说服老板迁库先在测试环境跑一周典型业务看看那几条最磨人的查询有没有变化。最后再分享一个小习惯每次升级大版本之后可以在query_log里保留一份“升级前慢查询 Top 30”的清单升级后逐个对比这样即使出了问题也有一手数据可以参考而不是靠猜。