zl程序教程

您现在的位置是:首页 >  Java

当前栏目

客快物流大数据项目(九十六):ClickHouse的VersionedCollapsingMergeTree深入了解

2023-02-18 16:46:26 时间

ClickHouse的VersionedCollapsingMergeTree深入了解

该引擎继承自 MergeTree 并将折叠行的逻辑添加到合并数据部分的算法中,这个引擎:

  • 允许快速写入不断变化的对象状态
  • 删除后台中的旧对象状态,这显著降低了存储体积

VersionedCollapsingMergeTree 用于相同的目的折叠树 ,但使用不同的折叠算法,允许以多个线程的任何顺序插入数据。 特别是, Version 列有助于正确折叠行,即使它们以错误的顺序插入。

相比之下, CollapsingMergeTree 只允许严格连续插入。

一、创建VersionedCollapsingMergeTree引擎表的语法

语法结构

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

VersionedCollapsingMergeTree(sign)参数说明

Sign是列名称,必须是Int8类型,用来标志Sign列。Sign列值为1是状态行,为-1是取消行。

二、折叠数据

考虑一种情况,您需要为某个对象保存不断变化的数据。对于一个对象有一行,并在发生更改时更新该行是合理的。但是,对于数据库管理系统来说,更新操作非常昂贵且速度很慢,因为它需要重写存储中的数据。 如果需要快速写入数据,则不能接受更新,但可以按如下顺序将更改写入对象。

使用 Sign 列写入行时。 如果 Sign = 1 这意味着该行是一个对象的状态(让我们把它称为 “state” 行)。 如果 Sign = -1 它指示具有相同属性的对象的状态的取消(让我们称之为 “cancel” 行)。 还可以使用 Version 列,它应该用单独的数字标识对象的每个状态。

例如,我们要计算用户在某个网站上访问了多少页面以及他们在那里的时间。

在某个时间点,我们用用户活动的状态写下面的行:

在稍后的某个时候,我们注册用户活动的变化,并用以下两行写入它。

第一行取消对象(用户)的先前状态。 它应该复制已取消状态的所有字段,除了 Sign.

第二行包含当前状态。因为我们只需要用户活动的最后一个状态行

可以删除,折叠对象的无效(旧)状态。 VersionedCollapsingMergeTree 在合并数据部分时执行此操作。

三、使用示例

创建表:

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

插入数据:

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

我们用两个 INSERT 查询以创建两个不同的数据部分。 如果我们使用单个查询插入数据,ClickHouse将创建一个数据部分,并且永远不会执行任何合并。

获取数据:

SELECT *  FROM UAct

我们在这里看到了什么,折叠的部分在哪里?

我们使用两个创建了两个数据部分 INSERT 查询。

该 SELECT 查询是在两个线程中执行的,结果是行的随机顺序。

由于数据部分尚未合并,因此未发生折叠。 ClickHouse在我们无法预测的未知时间点合并数据部分。

这就是为什么我们需要聚合:

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

如果我们不需要聚合,并希望强制折叠,我们可以使用 FINAL 修饰符 FROM

SELECT * FROM UAct FINAL

这是一个非常低效的方式来选择数据。 不要把它用于数据量大的表。