首页/数据/NoSQL 速查/CollapsingMergeTree 折叠变更新
ClickHouse56 条

CollapsingMergeTree 折叠变更新

CollapsingMergeTree 用一个 Int8 的 Sign 列表达增删:+1 状态行与 -1 取消行在合并时抵消,适合保留变更轨迹的流水场景;读取未折叠数据可用 sum(Sign) 修正。

NoSQL 速查数据更新于 2026-10-13

CollapsingMergeTree 是什么

CollapsingMergeTree 用一个 Int8 的 Sign 列表达增删:+1 是状态行,-1 是对应的取消行,合并时同排序键的一对行互相抵消,数据量随时间收敛。它适合既要最新状态又保留变更轨迹的流水场景。查询尚未完成折叠的数据时要用 sum(Sign) 修正:指标乘 Sign 再求和,并用 HAVING sum(Sign) > 0 过滤掉已取消的记录。VersionedCollapsingMergeTree 用版本列处理乱序到达。

基本信息

引擎参数CollapsingMergeTree(sign),sign 取 1 或 -1
合并行为同排序键的一对 +1/-1 行互相抵消
读取修正sum(指标 * Sign),并按 HAVING sum(Sign) > 0 过滤
替代变体VersionedCollapsingMergeTree(sign, version)
所属系统ClickHouse

典型使用场景

  • 保留变更历史:既要最新状态也要轨迹的流水表
  • 数据收敛:频繁更新的计数类数据通过抵消控制体积
  • 状态表:积分、余额这类需要保留增减记录的场景
  • 乱序写入:用带版本列的变体处理到达顺序不确定的数据
  • 修正读取:查询未折叠数据时按 Sign 求和还原正确值

建表与读取修正

建表与读取修正
CREATE TABLE page_views
(
    user_id UInt64,
    views UInt64,
    Sign Int8
)
ENGINE = CollapsingMergeTree(Sign)
ORDER BY user_id;

-- 变更时写入取消行 + 新状态行(插入顺序遵循官方示例)
INSERT INTO page_views VALUES (1001, 7, 1);
INSERT INTO page_views VALUES (1001, 7, -1), (1001, 8, 1);

-- 未折叠数据按 Sign 修正读取
SELECT user_id, sum(views * Sign) AS v
FROM page_views
GROUP BY user_id
HAVING sum(Sign) > 0;

要素读法

要素含义注意
Sign 列1 有效,-1 取消类型必须是 Int8
排序键决定哪些行互相抵消与 ORDER BY 定义一致
抵消合并时成对消掉依赖合并,未合并时仍有多行
读取修正指标乘 Sign 求和不加修正会重复计数
版本列判断乱序数据新旧属于 Versioned 变体
折叠依赖合并过程,未合并时同一主键会出现多行;业务查询不写 FINAL 时用上面的标准修正写法即可拿到正确结果。
插入顺序错误会导致折叠结果与预期不符,落地前对照官方示例验证一次,尤其是取消行与新状态行的先后顺序。
频繁写入取消行会放大写入量,如果只需要最新状态而不关心变更轨迹,ReplacingMergeTree 更省事。
FINAL 修饰符能在查询时完成折叠,但会带来额外的内存与计算开销;明细量大时优先使用按 Sign 修正的写法。

易错点

  • 插入顺序错误会导致折叠结果不符预期,落地前对照官方示例验证一次
  • 频繁的取消行会放大写入量,纯最新状态场景优先 ReplacingMergeTree
  • 读取时不加 Sign 修正会重复计数,报表与明细对不上
  • 把 Sign 列设计成其它类型或填入其它取值,会导致合并无法正确抵消

数据来源与参考

  • ClickHouse Docs: CollapsingMergeTree
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌