暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

快到飞起的分析数据库ClickHouse笔记-MergeTree

见白 2020-12-22
635


                                                    

ClickHouse中最常用也最强大的表引擎就是合并树,即MergeTree,以及该合并树家族下的系列引擎(*MergeTree)。下面对该系列的常见引擎窥其一二。

1. MergeTree家族

1.1 系列引擎    

        clickhouse的MergeeTree家族中除了基础表引擎MergeTree之外,常用的表引擎还有:


  • ReplacingMergeTree

  • SummingMergeTree

  • AggregatingMergeTree

  • CollapsingMergeTree

  • VersionedCollapsingMergeTree

  • ReplacingMergeTree

  • SummingMergeTree

  • AggregatingMergeTree

  • CollapsingMergeTree

  • VersionedCollapsingMergeTree


        这些表引擎都属于MergeTree家族,名称都以MergeTree后缀结尾。相比于MergeTree引擎,它们的所有特殊逻辑,都是在触发合并的过程中被激活的。

1.2 数据TTL定义

        clickhouse支持TTL,即Time To Live,存活时间。具体分为列级别的TTL和表级别的TTL。

        列级别的ttl直接在定义列名时加上TTL及时间:

        c0 DateTime,

c1 String TTL  c0 + INTERVAL 10 SECOND

        列级别的ttl修改:

alter table new_table modify column c1 String TTL c0 + INTERVAL 10 SECOND


*    无法取消列级别的ttl


        表级别的ttl直接在定义表结构时加上TTL及时间:

        order by create_time

TTL create_time + INTERVAL 1 DAY


*    同样,无法取消表级别的TTL

1.3 数据TTL原理

  • MergeTree以分区目录为单位,通过ttl.txt文件记录过期时间。

  • 每写入一批数据,会基于INTERVAL表达式的计算结果为这个分区生成ttl. txt文件。

  • 只有在MergeTree合并分区时,才触发删除TTL过期数据。

  • 删除分区使用贪婪算法,尽可能找到会最早过期的,同时年纪又是最老的分区(合并次数更多,MaxBlockNum更大的)。

  • 整列TTL过期合并后不包含该列.bin文件。

  • TTL合并频率由MergeTree的merge_with_ttl_timeout参数控,默认1天。维护独立的TTL任务队列,值过小则性能损耗。

  • 主动强制触发合并:

    optimize table new_table //一个分区

    optimize table new_table final //所有分区

  • 全局TTL合并任务开关:system stop/start TTL merges

1.4 多路径存储策略

        目前clickhouse支持三种数据存储策略的设置:


  • 默认策略:

    config.xml配置中path指定的路径。

  • JBOD策略:

    适合挂载了多块磁盘,但没有做RAID的场景。全称Just a Bunch of Disks,轮询策略,每次INSERT或者MERGE,新分区会轮询写入各个磁盘。降低磁盘负载,提高并行读写性能。单磁盘容错机制。

  • HOT/COLD策略:

    适合挂载了不同类型磁盘场景。冷热分区,hot使用ssd,cold使用hdd。写入MergeTree是保存在hot,数据累计后自行移动到cold。hot、cold区域内可定义多磁盘实行jbod策略。具体的工作方式是由多个磁盘卷(volume卷)组成了一个volume组。每当生成一个新数据分区的时候,按照阈值大小(max_data_part_size),分区目录会依照volume组中磁盘卷定义的顺序,依次轮询并写入各个卷下的磁盘。


*    自定义

        目前的MergeTree的存储策略目前不支持修改,但是分区目录却支持移动。可以根据具体的数据,以数据分区为最小移动单元,将分区目录写入多块磁盘目录。      

 

  • 将某个分区移动至当前存储策略中当前volume卷下的其他disk磁盘:

alter table hot_cold_table move part 'all_1_2_1' to disk 'disk_hot1';

  • 将某个分区移动至当前存储策略中其他的volume卷:

alter table hot_cold_table move part 'all_1_2_1' to volume 'cold';


        clickhouse支持数据库中查看磁盘策略,查询系统表system.storage_policies,其中的max_data_part_size参数表示分区大小限制,超过该值移动到紧邻的下一个磁盘。查询系统表system.parts,其中的name参数表示分区名;disk_name参数表示分区所在的磁盘。

2. 各个击破


本节详细介绍MergeTree家族中常见的表引擎。

2.1 ReplacingMergeTree

        MergeTree 引擎中表的主键是不唯一的,为了使得主键唯一,出现了ReplacingMergeTree 表引擎。ReplacingMergeTree 在合并分区时会删除主键相同的重复的数据,且是以分区为单位删除重复数据,在一定程度上达到了去重的效果。

        表定义时添加:

engine = ReplacingMergeTree(ver)

        其中的ver是选填的版本号,选择数值类的作为版本号,决定去重算法。


        原理:

(1)使用ORBER BY排序键作为判断重复数据的唯一键。

(2)只有在合并分区的时候才会触发删除重复数据的逻辑。

(3)以数据分区为单位删除重复数据。当分区合并时,同一分区内的重复数据会被删除;不同分区之间的重复数据不会被删除。

(4)在进行数据去重时,分区内的数据已经基于ORBER BY排序,可以找到那些相邻的重复数据。

(5)数据去重策略有两种:

如果没有设置ver版本号,则保留同一组重复数据中的最后一行;

如果设置了ver版本号,则保留同一组重复数据中ver字段取值最大的那一行。

2.2 SummingMergeTree

        当合并 SummingMergeTree 表的数据片段时,ClickHouse 会把所有具有相同主键的行合并为一行,该行包含了被合并的行中具有数值数据类型的列的汇总值。如果主键的组合方式使得单个键值对应于大量的行,则可以显著的减少存储空间并加快数据查询的速度。

        首先理清主键和排序键的关系:SummingMergeTree与AggregatingMergeTree的聚合都是根据ORDER BY进行的,同时声明了ORDER BY与PRIMARY KEY, MergeTree会强制要求PRIMARYKEY列字段必须是ORDER BY的前缀。这样就保障了即便在两者定义不同的情况下,主键仍然是排序键的前缀,不会出现索引与数据顺序混乱的问题

        ORDER BY是一项关键配置,SummingMergeTree在进行数据汇总时,会根据ORDER BY表达式的取值进行聚合操作。不同分区之间,数据不会被汇总合并。


        原理:

(1)用ORBER BY排序键作为聚合数据的条件Key。

(2)只有在合并分区的时候才触发汇总的逻辑。

(3)以分区为单位来聚合数据。当分区合并时,同一数据分区内聚合Key相同的数据会被合并汇总,而不同分区之间的数据则不会被汇总。

(4)如果在定义引擎时指定了columns汇总列(非主键的数值类型字段),则SUM汇总这些列字段;如果未指定,则聚合所有非主键的数值类型字段。

(5)在进行数据汇总时,因为分区内的数据已经基于ORBER BY排序,所以能够找到相邻且拥有相同聚合Key的数据。

(6)在汇总数据时,同一分区内,相同聚合Key的多行数据会合并成一行。其中,汇总字段会进行SUM计算;对于那些非汇总字段,则会使用第一行数据的取值

(7)支持嵌套结构,但列字段名称必须以Map后缀结尾。嵌套类型中,默认以第一个字段作为聚合Key。除第一个字段以外,任何名称以Key、Id或Type为后缀结尾的字段,都将和第一个字段一起组成复合Key

2.3 AggregatingMergeTree

        ClickHouse 会将一个数据片段内所有具有相同主键(准确的说是排序键)的行替换成一行,这一行会存储一系列聚合函数的状态。

       可以使用该引擎来做增量数据的聚合统计,包括物化视图的数据聚合。类似数据立方体,将规划好的维度数据聚合分析存储,便于快速查询。

        该引擎以二进制的形式存储中间状态结果。在合并分区的时候,按照预先定义的条件聚合数据。根据预先定义的聚合函数计算数据并通过二进制的格式存入表内。同时定义ORDER BY与PRIMARY KEY。写入数据时,需要调用*State函数;查询数据时,则需要调用相应的*Merge函数。

           常见的应用方式是结合物化视图使用,将它作为物化视图的表引擎。示例:


明细表(底表):

create table agg_table_basic (

id String,

city String,

code String,

value UInt32

)ENGINE = MergeTree()

partition by city

order by (id,city)


物化视图:

create materialized view agg_view

ENGINE = AggregatingMergeTree()

partition by city

order by (id,city)

AS SELECT 

id,

city,

uniqState(code) AS code,

sumState(value) AS value

FROM agg_table_basic

GROUP BY id,city

        

        底表负责写入原数据,物化视图负责查询。当底表有新的数据写入时,数据会自动在物化视图中按照引擎规则同步,客户端向物化视图发起查询,获取更高的查询性能。


        原理:

(1)用ORBER BY排序键作为聚合数据的条件Key。

(2)使用AggregateFunction字段类型定义聚合函数的类型以及聚合的字段。

(3)只有在合并分区的时候才会触发聚合计算的逻辑。

(4)以数据分区为单位来聚合数据。当分区合并时,同一数据分区内聚合Key相同的数据会被合并计算,而不同分区之间的数据则不会被计算。

(5)在进行数据计算时,因为分区内的数据已经基于ORBER BY排序,所以能够找到那些相邻且拥有相同聚合Key的数据。

(6)在聚合数据时,同一分区内,相同聚合Key的多行数据会合并成一行。对于那些非主键、非AggregateFunction类型字段,则会使用第一行数据的取值。

(7)AggregateFunction类型的字段使用二进制存储,在写入数据时,需要调用*State函数;而在查询数据时,则需要调用相应的*Merge函数。其中,*表示定义时使用的聚合函数。

(8)AggregatingMergeTree通常作为物化视图的表引擎,与普通MergeTree搭配使用。

2.4 CollapsingMergeTree

        CollapsingMergeTree 折叠合并书引擎会异步的删除(折叠)这些除了特定列 Sign 有 1 和 -1 的值以外,其余所有字段的值都相等的成对的行。没有成对的行将会被保留。因此,该引擎可以显著的降低存储量并提高 SELECT 查询效率。

        在CollapsingMergeTree表中,对行数据的修改删除,会转换成新增操作,以增代删,合并时统一删除。支持行级数据修改和删除的表引擎。定义一个sign标记位字段,记录数据行的状态。sign标记为1,则表示这是一行有效的数据;sign标记为-1,则表示这行数据需要被删除。分区合并时,同一数据分区内,sign标记为1和-1的一组数据会被抵消删除。


建表:

create table collap_table(

id String,

city String,

code String,

sign Int8

)ENGINE = CollapsingMergeTree(sign)

partition by city

order by (id,city)


        原理:

(1)如果sign=1比sign=-1的数据多一行,则保留最后一行sign=1的数据。

(2)如果sign=-1比sign=1的数据多一行,则保留第一行sign=-1的数据。

(3)如果sign=1和sign=-1的数据行一样多,并且最后一行是sign=1,则保留第一行sign=-1和最后一行sign=1的数据。

(4)如果sign=1和sign=-1的数据行一样多,并且最后一行是sign=-1,则什么也不保留。

(5)其余情况,ClickHouse会打印警告日志,但不会报错,在这种情形下,查询结果不可预知。


        数据不是实时抵消的,是在分区合并时执行。如果需要立即生效可以尝试以下方法:

(1)optimize TABLE table_name FINAL 命令可以强制分区合并,删除旧数据。

(2)改变查询sql:

select 

id,

sum(code * sign),

count(code * sign),

avg(code * sign),

uniq(code * sign) 

from 

collpase_table

group by id

havind sum(sign) > 0


*    相同分区才会抵消,一般数据都会在统一分区。

*    写入顺序有严格限制:

先写入sign=1,再写入sign=-1,正常折叠;

先写入sign=-1,再写入sign=1,不能够折叠;

多线程写入,不能保证顺序,不能保证折叠。

原因:表引擎机制决定的,要求sign=1和sign=-1的数据相邻。

2.5 VersionedCollapsingMergeTree

        该引擎的作用同CollapsingMergeTree,相比于CollapsingMergeTree只允许数据严格的连续插入,其没有顺序要求依旧可以正确折叠行。


建表:

create table collap_table(

id String,

city String,

code String,

sign Int8,

ver UInt8

)ENGINE = VersionedCollapsingMergeTree(sign, ver)

partition by city

order by (id,city)


        自动将ver作为排序条件并增加到ORDER BY的末端,每个分区内,数据按照ORDER BY id , ver DESC排序。所以无论写入时数据的顺序如何,在折叠处理都能回到正确的顺序。

3. 表引擎关系


本节介绍各引擎之间的关系。

3.1 继承关系

        MergeTree表引擎派生出6种引擎:


  • ReplacingMergeTree

  • SummingMergeTree

  • AggregatingMergeTree

  • CollapsingMergeTree

  • VersionedCollapsingMergeTree

  • GraphiteMergeTree


        MergeTree表引擎的MergingSortedBlockInputStream,共用MergeTree主体,主要区别在于Merge合并的部分,MergingSortedBlockInputStream的主要作用是按照ORDER BY的规则保持新分区数据的有序性。而其他6种变种MergeTree的合并逻辑,则是在有序的基础之上操作,要么是将排序后相邻的重复数据消除、要么是将重复数据累加汇总,等等。

3.2 组合关系

        ReplicatedMergeTree系列是在MergeTree能力的基础之上增加了分布式协同的能力,其借助ZooKeeper的消息日志广播功能,实现了副本实例之间的数据同步功能。以上介绍的7种表引擎都可以加上Replicated前缀,成为分布式表。




*    MergeTree引擎告一段落,下期将介绍其他的常见表引擎。




参考资料:

[1] Yandex.clickhouse官方文档[EB/OL]:https://clickhouse.tech/docs/en/

[2] 朱凯.ClickHouse原理解析与应用实践[M]







文章转载自见白,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论