ClickHouse(13)ClickHouse合并树MergeTree家族表引擎之CollapsingMergeTree详细解析

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
大数据开发治理平台 DataWorks,不限时长
简介: CollapsingMergeTree是ClickHouse的一种表引擎,它扩展了`MergeTree`,通过折叠行来优化存储和查询效率。当`Sign`列值为1和-1的成对行存在时,该引擎会异步删除除`Sign`外其他字段相同的行,只保留最新状态。建表语法中,`sign`列必须为`Int8`类型,用来标记状态(1)和撤销(-1)。写入时,应确保状态和撤销行的对应关系以保证正确折叠。查询时,可能需要使用聚合函数如`sum(Sign * x)`配合`GROUP BY`来处理折叠后的数据。使用`FINAL`修饰符可强制折叠,但效率较低。系列文章提供了更多关于ClickHouse及其表引擎的详细解析。

[TOC]

该引擎继承于MergeTree,并在数据块合并算法中添加了折叠行的逻辑。CollapsingMergeTree会异步的删除(折叠)这些除了特定列Sign有1和-1的值以外,其余所有字段的值都相等的成对的行。没有成对的行会被保留。因此,该引擎可以显著的降低存储量并提高SELECT查询效率。
简单来说就是,clickhouse会自动的合并有效和无效的数据,减少数据存储,并减少update所产生的性能消耗。具体的逻辑,下面介绍。

建表

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
    ...
) ENGINE = CollapsingMergeTree(sign)
[PARTITION BY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]

sign — 类型列的名称:1是«状态»行,也就是最后的有效行,-1是«取消»行,也就是无效行。列数据类型 — Int8。

创建CollapsingMergeTree表时,需要与创建 MergeTree 表时相同的子句。

折叠

数据

考虑你需要为某个对象保存不断变化的数据的情景。似乎为一个对象保存一行记录并在其发生任何变化时更新记录是合乎逻辑的,但是更新操作对DBMS来说是昂贵且缓慢的,因为它需要重写存储中的数据。如果你需要快速的写入数据,则更新操作是不可接受的,但是你可以按下面的描述顺序地更新一个对象的变化。

在写入行的时候使用特定的列Sign。如果Sign=1则表示这一行是对象的状态,我们称之为«状态»行。如果Sign=-1则表示是对具有相同属性的状态行的取消,我们称之为«取消»行。

例如,我们想要计算用户在某个站点访问的页面页面数以及他们在那里停留的时间。在某个时候,我们将用户的活动状态写入下面这样的行。

┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 432418202146624949451461 │
└─────────────────────┴───────────┴──────────┴──────┘

一段时间后,我们写入下面的两行来记录用户活动的变化。

┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 43241820214662494945146-1 │
│ 432418202146624949461851 │
└─────────────────────┴───────────┴──────────┴──────┘

第一行取消了这个对象(用户)的状态。它需要复制被取消的状态行的所有除了Sign的属性。

第二行包含了当前的状态。因为我们只需要用户活动的最后状态,这些行可以在折叠对象的失效(老的)状态的时候被删除。CollapsingMergeTree会在合并数据片段的时候做这件事。

┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 432418202146624949451461 │
│ 43241820214662494945146-1 │
└─────────────────────┴───────────┴──────────┴──────┘

这种方法的特殊属性

  1. 写入的程序应该记住对象的状态从而可以取消它。«取消»字符串应该是«状态»字符串的复制,除了相反的Sign。它增加了存储的初始数据的大小,但使得写入数据更快速。
  2. 由于写入的负载,列中长的增长阵列会降低引擎的效率。数据越简单,效率越高。
  3. SELECT的结果很大程度取决于对象变更历史的一致性。在准备插入数据时要准确。在不一致的数据中会得到不可预料的结果,例如,像会话深度这种非负指标的负值。

算法

当ClickHouse合并数据片段时,每组具有相同主键的连续行被减少到不超过两行,一行Sign=1(«状态»行),另一行Sign=-1(«取消»行),换句话说,数据项被折叠了。

对每个结果的数据部分ClickHouse保存的算法

  1. 如果«取消»和«状态»行数量相同,并且最后一行«状态»行,保留第一个«取消»和最后一个«状态»行。
  2. 如果«状态»行比«取消»行多一个或一个以上,保留最后一个«状态»行。
  3. 如果«取消»行比«状态»行多一个或一个以上,保留第一个«取消»行。
  4. 没有行,在其他所有情况下。合并会继续,但ClickHouse会把此情况视为逻辑错误并将其记录在服务日志中。这个错误会在相同的数据被插入超过一次时出现。

因此,折叠不应该改变统计数据的结果。变化逐渐地被折叠,因此最终几乎每个对象都只剩下了最后的状态。

Sign是必须的因为合并算法不保证所有有相同主键的行都会在同一个结果数据片段中,甚至是在同一台物理服务器上。ClickHouse用多线程来处理SELECT请求,所以它不能预测结果中行的顺序。如果要从CollapsingMergeTree表中获取完全«折叠»后的数据,则需要聚合。

要完成折叠,请使用GROUP BY子句和用于处理符号的聚合函数编写请求。例如,要计算数量,使用sum(Sign)而不是 count()。要计算某物的总和,使用sum(Sign * x)而不是sum(x),并添加HAVING sum(Sign) > 0子句。

聚合体count,sum和avg可以用这种方式计算。如果一个对象至少有一个未被折叠的状态,则可以计算uniq聚合。min和 max聚合无法计算,因为CollaspingMergeTree不会保存折叠状态的值的历史记录。

如果你需要在不进行聚合的情况下获取数据(例如,要检查是否存在最新值与特定条件匹配的行),你可以在 FROM 从句中使用 FINAL 修饰符。这种方法显然是更低效的。

# 示例:

┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 432418202146624949451461 │
│ 43241820214662494945146-1 │
│ 432418202146624949461851 │
└─────────────────────┴───────────┴──────────┴──────┘

# 建表:

CREATE TABLE UAct
(
    UserID UInt64,
    PageViews UInt8,
    Duration UInt8,
    Sign Int8
)
ENGINE = CollapsingMergeTree(Sign)
ORDER BY UserID

# 插入数据:

INSERT INTO UAct VALUES (4324182021466249494, 5, 146, 1)

INSERT INTO UAct VALUES (4324182021466249494, 5, 146, -1),(4324182021466249494, 6, 185, 1)

#我们使用两次INSERT请求来创建两个不同的数据片段。如果我们使用一个请求插入数据,ClickHouse只会创建一个数据片段且不会执行任何合并操作。

#获取数据:

SELECT * FROM UAct

┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 43241820214662494945146-1 │
│ 432418202146624949461851 │
└─────────────────────┴───────────┴──────────┴──────┘
┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 432418202146624949451461 │
└─────────────────────┴───────────┴──────────┴──────┘



#我们看到了什么,哪里有折叠?

#通过两个 INSERT 请求,我们创建了两个数据片段。
#SELECT请求在两个线程中被执行,我们得到了随机顺序的行。
#没有发生折叠是因为还没有合并数据片段。
#ClickHouse 在一个我们无法预料的未知时刻合并数据片段。

#因此我们需要聚合:

SELECT
    UserID,
    sum(PageViews * Sign) AS PageViews,
    sum(Duration * Sign) AS Duration
FROM UAct
GROUP BY UserID
HAVING sum(Sign) > 0

┌──────────────UserID─┬─PageViews─┬─Duration─┐
│ 43241820214662494946185 │
└─────────────────────┴───────────┴──────────┘

# 如果我们不需要聚合并想要强制进行折叠,我们可以在 FROM 从句中使用 FINAL 修饰语。

SELECT * FROM UAct FINAL

┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐
│ 432418202146624949461851 │
└─────────────────────┴───────────┴──────────┴──────┘

# 这种查询数据的方法是非常低效的。不要在大表中使用它。

资料分享

ClickHouse经典中文文档分享

clickhouse系列文章

相关文章
|
9天前
|
存储 SQL NoSQL
ClickHouse(16)ClickHouse日志表引擎Log详细解析
ClickHouse的Log引擎系列适用于小数据量(<1M行)的表,包括StripeLog、Log和TinyLog。这些引擎将数据存储在磁盘,追加写入,不支持更新和索引,写入非原子可能导致数据损坏。Log和StripeLog支持并发访问和并行读取,Log按列存储,StripeLog将所有数据存于一个文件。TinyLog是最简单的,不支持并行读取和并发访问,每列存储在单独文件中。适用于一次性写入、多次读取的场景。
17 0
|
10天前
|
传感器 存储 SQL
ClickHouse(15)ClickHouse合并树MergeTree家族表引擎之GraphiteMergeTree详细解析
GraphiteMergeTree是ClickHouse用于优化Graphite数据存储和汇总的表引擎,适合需要瘦身和高效查询Graphite数据的开发者。它基于MergeTree,减少存储空间并提升查询效率。创建表时需包括Path、Time、Value和Version列。配置涉及pattern、regexp、function和retention,用于指定聚合函数和数据保留规则。文章还提供了建表语句示例和相关资源链接。
11 1
|
10天前
|
存储 SQL 数据挖掘
【源码解析】使用 Pandas 优化数据存储:深入解析 Block 合并机制
【源码解析】使用 Pandas 优化数据存储:深入解析 Block 合并机制
|
11天前
|
机器学习/深度学习 人工智能 自然语言处理
智能时代的引擎:深度学习技术解析
【6月更文挑战第8天】本文深入探讨了深度学习技术,一种基于人工神经网络的机器学习方法。我们将从其基本原理出发,分析其在数据处理、特征提取和模式识别方面的强大能力。文章将通过具体案例,展示深度学习在图像识别、自然语言处理等领域的应用,并讨论其面临的挑战与未来发展方向。
|
13天前
|
存储 SQL 算法
ClickHouse(14)ClickHouse合并树MergeTree家族表引擎之VersionedCollapsingMergeTree详细解析
VersionedCollapsingMergeTree是ClickHouse的一种优化引擎,扩展了MergeTree,支持多线程异步插入和高效的数据折叠。它通过Sign和Version列处理对象状态的变化,Sign表示行的状态(正向或撤销),Version追踪状态版本。引擎自动删除旧状态,减少存储占用。在查询时,需注意可能需使用GROUP BY和聚合函数确保数据折叠,因为ClickHouse不保证查询结果已折叠。文章还提供了建表语法、使用示例和相关资源链接。
16 0
|
1天前
|
Java Spring
深入解析Spring源码,揭示JDK动态代理的工作原理。
深入解析Spring源码,揭示JDK动态代理的工作原理。
7 0
|
1天前
|
XML Java 数据格式
深度解析 Spring 源码:揭秘 BeanFactory 之谜
深度解析 Spring 源码:揭秘 BeanFactory 之谜
5 1
|
8天前
|
安全 Java 数据安全/隐私保护
Java基础4-一文搞懂String常见面试题,从基础到实战,更有原理分析和源码解析!(二)
Java基础4-一文搞懂String常见面试题,从基础到实战,更有原理分析和源码解析!(二)
15 0
|
8天前
|
JSON 安全 Java
Java基础4-一文搞懂String常见面试题,从基础到实战,更有原理分析和源码解析!(一)
Java基础4-一文搞懂String常见面试题,从基础到实战,更有原理分析和源码解析!(一)
19 0
|
9天前
|
Java

推荐镜像

更多