导读:本期聚焦于小伙伴创作的《ClickHouse 的 MergeTree 引擎家族该怎么选:Replacing、Summing 还是 Aggregating?》,敬请观看详情。在实时数仓建设中,明细数据去重、指标预聚合和状态更新是三类典型诉求。ReplacingMergeTree 通过版本字段保留最新记录,适合缓慢变化维与订单状态流转;SummingMergeTree 在后台合并时累加数值列,能显著降低求和查询的扫描量;AggregatingMergeTree 则借助物化视图将 count、uniq 等聚合中间状态落盘,支撑高并发多维汇总。三者均继承 MergeTree 的稀疏索引与分区能力,但写入放大、查询语义和最终一致性表现差异明显。理解后台 merge 时机与查询层补全逻辑,才能避免误用导致数据重复或指标偏差。

ClickHouse 的 MergeTree 系列引擎是支撑海量数据分析的核心存储结构。当业务不再满足于只做追加式明细存储,而是希望减少重复数据、提前汇总指标时,就会在 ReplacingMergeTree、SummingMergeTree 与 AggregatingMergeTree 之间做权衡。它们并非相互替代,而是分别解决了不同维度的问题。

ClickHouse 的 MergeTree 引擎家族该怎么选:Replacing、Summing 还是 Aggregating?

一、ReplacingMergeTree:按版本保留最新记录

ReplacingMergeTree 的设计目标是处理“相同主键对应多版本”的场景。比如在电商订单系统中,一个订单会因支付、发货、退款产生多条状态变更记录。如果直接写入普通 MergeTree,查询时会出现重复订单。ReplacingMergeTree 要求在建表时指定一个版本列(或时间戳列),后台在合并分区数据块时,会对相同排序键的记录只保留版本最大的一条。

需要注意的是,这种去重发生在后台 merge 阶段,而不是写入时。因此,在 merge 还没有执行的时间窗口内,用户仍然可能查到多条相同主键的记录。通常需要在查询层使用 FINAL 修饰符,或者借助视图做最新态补全。下面给出一个典型建表语句:

CREATE TABLE order_state
(
    order_id UInt64,
    status String,
    update_time DateTime,
    amount Decimal(10,2)
)
ENGINE = ReplacingMergeTree(update_time)
PARTITION BY toYYYYMM(update_time)
ORDER BY order_id;

上述表中,order_id 是排序键,update_time 作为版本列。当后台合并时,相同 order_id 的多条数据仅保留 update_time 最大的行。如果业务要求查询强一致的最新态,可以写 SELECT * FROM order_state FINAL,但 FINAL 会在查询时做额外归并,有一定性能开销。

从适用边界看,ReplacingMergeTree 不适合高频更新同一主键且要求毫秒级可见的场景,因为 merge 节奏由系统控制。它更擅长缓慢变化维、配置表同步、以及允许最终一致性的状态类数据。

二、SummingMergeTree:数值列的自动累加

当分析需求以“按维度求和”为主,例如统计每个城市的日销售额,而原始写入是逐笔交易时,SummingMergeTree 可以在后台合并时将指定数值列自动相加,从而减少查询时的实时计算量。建表时需通过 ORDER BY 定义维度列,并明确哪些列需要被求和。

与 ReplacingMergeTree 类似,求和也只在 merge 时发生。未合并的数据块中,相同维度仍然有多行。查询时如果直接 SUM(amount),ClickHouse 会对已合并的聚合行和未合并的明细行一起求和,最终结果正确,但扫描行数可能多于预期。示例如下:

CREATE TABLE city_sales
(
    city String,
    day Date,
    sales Decimal(12,2),
    cnt UInt32
)
ENGINE = SummingMergeTree()
PARTITION BY toYYYYMM(day)
ORDER BY (city, day);

在上面的结构中,city 与 day 组成排序键,sales 和 cnt 会在合并时分别累加。如果某天北京写入了三笔记录,后台 merge 后可能变成一行 (北京, 当天, 总销售额, 总笔数)。这能明显提升大时间范围汇总的效率。

但 SummingMergeTree 不会处理非数值列。如果排序键之外还有其它字符串字段,合并时系统会随机保留其中一行的值,因此不能依赖它做去重或最新值替换。它专用于“只关心合计、不关心明细差异”的指标的预计算。

三、AggregatingMergeTree:聚合中间状态的物化

AggregatingMergeTree 是三者中表达能力最强的一类。它不直接存最终结果,而是存储聚合函数的中间状态,例如 uniqState、sumState、avgState。配合物化视图,可以在数据写入明细表时,自动将聚合状态落到聚合表,查询时使用对应的 State 与 Merge 函数还原。

这种方式特别适合多维分析平台:前端可以按任意维度组合查询,而后台只需维护一份聚合表。因为中间状态可继续合并,所以即使数据分多批写入,最终聚合结果依然准确。典型用法如下:

CREATE TABLE uv_detail
(
    day Date,
    site String,
    user_id UInt64
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(day)
ORDER BY (day, site, user_id);

CREATE TABLE uv_agg
(
    day Date,
    site String,
    uv AggregateFunction(uniq, UInt64)
)
ENGINE = AggregatingMergeTree()
PARTITION BY toYYYYMM(day)
ORDER BY (day, site);

CREATE MATERIALIZED VIEW uv_mv
TO uv_agg
AS SELECT
    day,
    site,
    uniqState(user_id) AS uv
FROM uv_detail
GROUP BY day, site;

写入 uv_detail 后,物化视图会把每批数据的 uniqState 写入 uv_agg。查询时通过 uniqMerge(uv) 得到去重用户数。由于 AggregateFunction 类型支持再次合并,后台 merge 不会破坏精度。

相比在查询时直接 uniq(user_id),AggregatingMergeTree 把大量计算前置到写入和 merge 阶段,使大屏类高并发查询更加平稳。代价是物化视图逻辑较复杂,且聚合维度相对固定。

四、选型对比与建议

为了更直观地理解三者差异,可以从数据语义、合并行为和查询特点三个角度对比:

引擎类型核心语义合并时动作典型场景
ReplacingMergeTree保留主键最新版本同排序键留版本最大行订单状态、配置同步
SummingMergeTree数值列求和同排序键数值累加销售额、点击量汇总
AggregatingMergeTree存聚合中间状态同排序键状态合并UV、多维指标预计算

实践中,一个数仓往往组合使用:原始层用 MergeTree 存明细,中间层用 SummingMergeTree 或 AggregatingMergeTree 做轻度汇总,维度表用 ReplacingMergeTree 保证最新。切忌把 ReplacingMergeTree 当作实时更新数据库使用,也避免用 SummingMergeTree 承载需要保留明细的文本字段。

另外,所有衍生引擎都依赖后台 merge,因此分区粒度、写入批大小和 merge 线程配置都会影响去重与聚合的可见延迟。建议在测试环境观察 system.parts 中活跃数据块数量,再决定分区策略。

五、常见误区提醒

不少团队误以为使用了 ReplacingMergeTree 就无需在查询时处理重复,结果在仪表盘上看到翻倍指标。根本原因是忽略了 FINAL 与未合并 parts 的存在。若查询频率高且对重复零容忍,应在服务层封装带 FINAL 的查询,或定期 optimize 分区(仅适合小表)。

还有人用 SummingMergeTree 存储用户维度信息,期望合并后留下想要的描述字段,实际却得到随机值,引发数据质量投诉。牢记:SummingMergeTree 只保证数值列语义,非数值列合并行为未定义。正确做法是将维度属性放维度表,用 ReplacingMergeTree 管理,事实表只保留外键与度量。

总体来看,选型的关键不是“哪个更高级”,而是“数据在业务里如何被消费”。先画出读写路径与查询模式,再让引擎特性去匹配,才能发挥 ClickHouse 的极致性能。

ClickHouseMergeTreeReplacingMergeTree修改时间:2026-08-05 05:12:39

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。