描述
CollapsingMergeTree 引擎继承自 MergeTree,
并增加了在合并过程中折叠行的逻辑。
如果一对行在排序键 (ORDER BY) 中的所有字段都相同,只有特殊字段 Sign 不同,
且该字段的值分别为 1 和 -1,CollapsingMergeTree 表引擎就会异步删除 (折叠) 这对行。
没有与之配对且 Sign 值相反的行则会被保留。
更多详情,请参阅本文档中的 折叠 部分。
该引擎可以显著减少存储量,
从而提高
SELECT 查询的效率。参数
Sign 参数外,此表引擎的所有参数
其含义均与 MergeTree 中相同。
Sign— 用作表示行类型的列名,其中1表示“状态”行,-1表示“取消行”。类型:Int8。
创建表
折叠
数据
Sign。
- 如果
Sign=1,表示该行是“状态行”:包含表示当前有效状态的各个字段的一行。 - 如果
Sign=-1,表示该行是“取消行”:用于抵消具有相同属性的对象状态的一行。
Sign 之外的所有排序键字段。
上面的第二行表示当前状态。
由于我们只需要用户活动的最终状态,因此可以按如下所示删除原始“状态行”以及我们插入的“取消”
行,从而折叠对象的无效 (旧) 状态:
CollapsingMergeTree 会在数据分区片段合并期间执行这种折叠行为。
关于为什么每次变更都需要两行,
将在 算法 一节中进一步说明。
- 写入数据的程序必须记住对象的状态,才能将其取消。“取消行”应包含“状态行”中排序键字段的副本,以及相反的
Sign。这会增大初始存储空间占用,但能让数据写入更快。 - 列中持续增长的长数组会因写入负载增加而降低引擎效率。数据越简单,效率越高。
SELECT结果在很大程度上取决于对象变更历史的一致性。准备插入数据时务必谨慎;数据不一致时,结果可能不可预测。例如,像会话深度这类非负指标可能会出现负值。
算法
ORDER BY) 的每组连续行都会被归并为不超过两行,
即 Sign = 1 的“状态行”和 Sign = -1 的“取消行”。
换句话说,在 ClickHouse 中,这些条目会发生折叠。
对于每个结果数据分区片段,ClickHouse 会保存:
此外,当“状态行”比“取消行”至少多两行,
或者“取消行”比“状态行”至少多两行时,合并会继续进行。
不过,ClickHouse 会将这种情况视为逻辑错误,并将其记录到服务器日志中。
如果同一份数据被多次 insert,就可能出现此错误。
因此,折叠不应改变统计信息的计算结果。
这些变更会逐步折叠,因此最终几乎每个对象都只会保留最后一个状态。
之所以需要
Sign 列,是因为合并算法无法保证
具有相同排序键的所有行都会出现在同一个结果数据分区片段中,甚至不一定在同一台物理服务器上。
ClickHouse 使用多个线程处理 SELECT 查询,因此无法预测结果中各行的顺序。
如果需要从 CollapsingMergeTree 表中获取完全“折叠”后的数据,则必须进行聚合。
要完成最终折叠,请编写带有 GROUP BY 子句和会考虑符号的聚合函数的查询。
例如,要计算数量,请使用 sum(Sign) 而不是 count()。
要计算某个值的总和,请使用 sum(Sign * x) 并结合 HAVING sum(Sign) > 0,而不是使用 sum(x),
如下方示例所示。
聚合 count、sum 和 avg 可以用这种方式计算。
如果某个对象至少有一个未折叠的状态,则聚合 uniq 也可以计算。
而聚合 min 和 max 则无法计算,
因为 CollapsingMergeTree 不会保存已折叠状态的历史记录。
如果你需要在不进行聚合的情况下提取数据
(例如,检查最新值满足特定条件的行是否存在) ,
你可以在
FROM 子句中使用 FINAL 修饰符。它会在返回结果前先合并数据。
对于 CollapsingMergeTree,每个键只会返回最新的状态行。示例
用法示例
CollapsingMergeTree 创建一个 UAct 表:
INSERT 查询来创建两个不同的数据分区片段。
如果通过单条查询插入数据,ClickHouse 只会创建一个数据分区片段,并且之后将不会再执行任何合并。
INSERT 查询,我们创建了两个数据分区片段。
SELECT 查询在两个线程中执行,因此得到的行顺序是随机的。
不过,折叠 并未发生,因为数据分区片段此时还没有发生 merge,
而 ClickHouse 会在后台的某个不可预知时间对数据分区片段进行 merge,我们无法提前预测。
因此,我们需要进行 聚合,
这可以通过 sum
aggregate function 和 HAVING clause 来实现:
FROM 子句中使用 FINAL 修饰符。
这种数据选择方式效率较低,不建议在扫描大量数据 (数百万行) 时使用。
另一种方法的示例
Sign 列。
在此示例中,我们将使用下面的样本数据:
PageViews 和 Duration 的数据类型更改为可存储负值。
因此,在使用
collapsingMergeTree 创建 UAct 表时,我们将这些列的数据类型从 UInt8 更改为 Int16: