概述
1、MergeTree
1.1数据TTL
1.1.1列级别TTL
-- 创建一张包含列级别TTL的表
create table test.ttl_table_v1 (
id String, -- 主键字段不能被声明TTL表达式
create_time DateTime,
-- 需要依托对某个DateTime或Date类型字段的INTERVAL操作,来表达TTL的过期时间
code String TTL create_time + INTERVAL 10 SECOND, -- 当系统时间超过该TTL超时时间则字段会被清理并且设置为字段类型的默认值
type UInt8 TTL create_time + INTERVAL 10 SECOND
)ENGINE = MergeTree()
PARTITION BY toYYYYMM(create_time)
ORDER BY id;
insert into table test.ttl_table_v1 values('A000', now(), 'C1', 1),
('A000', now()+ INTERVAL 10 MINUTE, 'C1', 1);
select * from test.ttl_table_v1;
-- 执行强制触发TTL清理
optimize table test.ttl_table_v1 final;
-- 修改表的TTL字段或者新增TTL字段
alter table test.ttl_table_v1 modify column code string TTL create_time + INTERVAL 1 DAY
1.1.2表级别TTL
create table test.ttl_table_v2 (
id String,
create_time DateTime,
code String TTL create_time + INTERVAL 10 SECOND,
type UInt8
)ENGINE = MergeTree()
PARTITION BY toYYYYMM(create_time)
ORDER BY create_time
-- 这里定义表级别的TTL,当出发TTL清除的时候,满足过期时间的数据行将会被删除
TTL create_time + INTERVAL 1 DAY;
-- 新增或者修改表级别的TTL
alter table test.ttl_table_v1 modify TTL create_time + INTERVAL 3 DAY
1.1.3TTL运行机制
当设置表级别的TTL后,在写入数据的时候,会以数据分区为单位,在每个分区的目录里面生成一个ttl.txt的文件
[root@localhost test]# tree ttl_table_v2/
ttl_table_v2/
├── 202103_1_1_0
│ ├── checksums.txt
│ ├── columns.txt
│ ├── count.txt
│ ├── data.bin
│ ├── data.mrk3
│ ├── default_compression_codec.txt
│ ├── minmax_create_time.idx
│ ├── partition.dat
│ ├── primary.idx
│ └── ttl.txt
├── detached
└── format_version.txt
ttl format version: 1
{"columns":[{"name":"code","min":0,"max":0},{"name":"type","min":0,"max":0}],"table":{"min":1615175460,"max":1615176099}}
"columns":[{"name":"code","min":0,"max":0}]是列级别ttl的描述信息。
"table":{"min":1615175460,"max":1615176099}是表界别的tt描述信息。
min,max分别保存了当前数据分区中,ttl指定日期字段的最小值、最大值分别于INTERVAL表达式计算后的时间戳。
处理逻辑:
1、MergeTree以分区目录为单位,通过ttl.txt文件记录过期时间,并将其作为后续的判断依据。
2、每当写入一批数据,都会基于INTERVAL表达式计算结果为整个分区生成ttl.txt文件。
3、只有在MergeTree合并分区时,才会触发删除TTL过期数据的逻辑。
4、在选择删除分区时,会使用贪婪算法,它的算法规则是尽可能多的找到会最早过期的,同事年纪有事最老的分区(合并次数最多,MaxBlockNum更大的)。
Tips:
1、TTL默认等待合并频率是由MergeTree的merge_with_ttl_timeout参数控制,默认是一天。如果这个值被设置过小会有性能问题。
2、除了自动出发合并,也可以手动合并
-- 触发一个分区合并 optimize table table_name -- 触发所有的分区合并 optimize table table_name final
3、目前没有提供删除TTL声明的方法,但是提供了控制全局TTL(不能按找表级别)合并任务的启停方法:
system stop/start ttl merges
1.2多路径存储策略
默认策略:所有的分区数据会自动保存到config.xml配置的path路径中去。
JBOD策略:适合服务器挂载了多块磁盘,但没有做RAID的场景。JBOD(just a bunch of disks)是一种轮询策略,每执行一次INSERT或者MERGE,所产生的新分区会轮询你写入各个磁盘,如果单块磁盘发生故障,则会丢掉应用JBOD策略写入的这部分数据。
HOT/COLD策略:适合服务器挂在了不同类型磁盘的场景(SSD/HDD)。将存储分期HOT和COLD两类区域。HOT区域使用SSD,COLD区域使用HDD。数据在写入MerGeTree之初,首先会在HOT区域创建分区目录用于保存数据,当分区数据大小累积到阈值时,数据会自行移动到COLD区域。而在每个区域的内部,也支持定义多块磁盘,所以在单个区域的写入过程中也能应用JBOD策略。
配置存储策略的在config.xml中时storage_configuration标签,下面还有disks(磁盘)和policy(策略)两个标签。
<storage_configuration>
<disks>
<!--自定义磁盘名字-->
<disk_name_a>
<!--定义磁盘的路径-->
<path>/clickhouse/data</path>
<!--定义磁盘的预留空间-->
<keep_free_space_bytes>4096</keep_free_space_bytes>
</disk_name_a>
</disks>
<policies>
<!--自定义策略名字-->
<policy_name_a>
<volumes>
<!--卷的自定义名称-->
<volume_name_a>
<!--关联的配置内的磁盘-->
<disk>disk_name_a</disk>
<disk>disk_name_b</disk>
<!--表示在这个卷的单个disk磁盘中,一个数据分区的最大存储阈值,如果当前分区的数据大小超过阈值,
则之后的分区会写入下一个disk磁盘-->
<max_data_part_size_part>4096</max_data_part_size_part>
</volume_name_a>
</volumes>
<!--如果当前卷的可用空间小于factor因子,并且定义了多个卷,则数据会自定向下一个卷移动-->
<move_factor>2.0</move_factor>
</policy_name_a>
</policies>
</storage_configuration>
1.2.1JBOD策略
<storage_configuration>
<disks>
<!--自定义磁盘名字-->
<disk_hot>
<!--定义磁盘的路径-->
<path>/clickhouse/data</path>
<!--定义磁盘的预留空间-->
<keep_free_space_bytes>4096</keep_free_space_bytes>
</disk_hot>
<disk_cold>
<!--定义磁盘的路径-->
<path>/clickhouse/data</path>
<!--定义磁盘的预留空间-->
<keep_free_space_bytes>4096</keep_free_space_bytes>
</disk_cold>
</disks>
<policies>
<!--自定义策略名字-->
<default_jbod>
<volumes>
<!--卷的自定义名称-->
<jbod>
<!--关联的配置内的磁盘-->
<disk>disk_hot</disk>
<disk>disk_cold</disk>
</jbod>
</volumes>
</default_jbod>
</policies>
</storage_configuration>
以上就配置好了JBOD存储策略了。在使用之前记得把两个存储目录的拥有者修改为clickhosue的用户,并且需要重启clickhouse-server服务。
执行以SQL可以查看配置的磁盘:
select
name,
path,
formatReadableSize(free_space) as free,
formatReadableSize(total_space) as total_space,
formatReadableSize(keep_free_space) as keep_free_space
from
system.disks;
执行以下SQL查看配置的存储策略:
select * from system.storage_policies;
使用配置的策略:
-- 创建表
create table test.jbod_table(
id UInt64
)engine = MergeTree()
Order By id
-- 配置表的存储策略
Setting storage_policy = 'default_jbod'
-- 写入数据
insert into table jbod_table select rand() from numbers(10);
-- 查看分区系统表, 分区存储在哪个磁盘上
select name, disk_name from system.parts where table = 'test.jbod_table';
总结:多个磁盘组成了一个磁盘组,即volume卷。每当生成一个新的数据分区的时候,分区目录会按照volume卷重磁盘定义的顺序,依次轮询写入各个磁盘。
1.2.2HOT/COLD策略
<storage_configuration>
<disks>
<!--自定义磁盘名字-->
<disk_hot>
<!--定义磁盘的路径-->
<path>/clickhouse/data</path>
<!--定义磁盘的预留空间-->
<keep_free_space_bytes>4096</keep_free_space_bytes>
</disk_hot>
<disk_cold>
<!--定义磁盘的路径-->
<path>/clickhouse/data</path>
<!--定义磁盘的预留空间-->
<keep_free_space_bytes>4096</keep_free_space_bytes>
</disk_cold>
</disks>
<policies>
<!--自定义策略名字-->
<moving_from_hot_to_cold>
<volumes>
<!--卷的自定义名称-->
<hot>
<!--关联的配置内的磁盘-->
<disk>disk_hot</disk>
<!--表示在这个磁盘下如果一个分区数据大小超过1M,则需要被移动到紧邻的下一个磁盘卷-->
<max_data_part_size>1073741824</max_data_part_size>
</hot>
<cold>
<disk>disk_cold</disk>
</cold>
</volumes>
<!--如果当前卷的可用空间小于factor因子,并且定义了多个卷,则数据会自定向下一个卷移动-->
<move_factor>2.0</move_factor>
</moving_from_hot_to_cold>
</policies>
</storage_configuration>
使用配置的策略:
-- 创建表
create table test.hot_cold_table(
id UInt64
)engine = MergeTree()
Order By id
-- 配置表的存储策略
Setting storage_policy = 'moving_from_hot_to_cold'
-- 写入数据
insert into table hot_cold_table select rand() from numbers(10);
-- 查看分区系统表, 分区存储在哪个磁盘上
select name, disk_name from system.parts where table = 'test.hot_cold_table';
Tips:
1、如果一次性写入1MB,分区也会被写入COLD卷中
2、多个磁盘卷组成一个磁盘组volume组,每当生成一个新数据分区的时候,按照阈值大小(max_data_part_size),分区目录会按照volume组中磁盘卷定义的顺序,依次轮询写入各个卷下的磁盘。
3、MergeTree的存储册罗目前不能修改,但是分区目录却可以移动。
-- 将某个分区移动到当前存储策略中当前卷下的其他的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"
2、ReplacingMergeTree
ReplacingMergeTree是为了去重而设计的,能够在分区合并的时候删除重复的数据。
使用时只需要把ENGINE = ReplacingMergeTree(ver),这里的ver是一个选填参数,会指定一个UInt*、Date或者DateTime类型的字段作为版本号,这个参数决定了数据去重时使用的算法。
-- 创建表
create table test.replace_table (
id String,
create_time DateTime,
code String
)ENGINE = ReplacingMergeTree()
PARTITION BY toYYYYMM(create_time)
ORDER BY (id, code)
primary key id;
-- 执行以下的命令2次及以上
insert into table test.replace_table values('A111', '2020-03-05 15:00:00', 'C1'),('A111', '2020-04-05 15:00:00', 'C1'),('A111', '2020-05-05 15:00:00', 'C1');
-- 然后手动清理表
optimize table test.replace_table final;
-- 然后查看表数据, 最后主键还是有重复的
select * from test.replace_table
Tips1:ReplacingMergeTree是以分区为单位删除重复数据的。只有在相同的数据分区重复的数据才可以删除,而不同的数据分区之间的重复数据依然不能被删除。
-- 创建表,这里加上版本号字段
create table test.replace_table (
id String,
create_time DateTime,
code String
)ENGINE = ReplacingMergeTree(create_time)
PARTITION BY toYYYYMM(create_time)
ORDER BY id
primary key id;
optimize table test.replace_table final;
Tips2:如果加上了版本号字段的话,则在删除重复数据的时候,会保留同一组数据内版本号列字段最大的哪一行。
总结:
(1)使用order by排序键作为判断数据重复的唯一键
(2)只有在合并分区的时候才会触发删除重复数据的逻辑
(3)以数据分区为单位删除重复数据。当分区合并时,同意分区内的重复数据会被删除;不同分区之间的数据不能被删除。
(4)在进行数据去重时,因为分区内的数据已经基于order by进行了排序,所以能够找到那些相邻的重复数据。
(5)数据去重的两种策略:
- 如果没有设置ver版本号,则保留同一组重复数据中的最后一行。
- 如果设置了ver版本号,则保留同一组重复数据中ver字段取值最大的那一行。
3、SummingMergeTree
SummingMergeTree的聚合是根据ORDER BY定义的排序键进行的。通常情况下只需要通过定义排序键就能定义了主键,但是如果同时定义了排序键和主键的话,则要求主键列字段必须是排序键的前缀。
当需要修改排序键的时候通过以下的命令:
alter table table_name modify order by (A, B)
注意:在修改排序键的时候,只能在原有的基础上减少字段,如果是新增排序字段的话,那只能是添加通过alter add column新增的字段。
SummingMergeTree使用方法:
ENGINE = SummingMergeTree((col1, col2, …))
Col1, col2为columns参数值,这是一个选填参数,用于设置除主键外的其他数值类型字段,以指定被SUM汇总的列字段。如果不填此参数,则会将所有除主键之外的数值类型进行SUM汇总。
create table summing_table (
id String,
city String,
v1 UInt32,
v2 Float64,
create_time Datetime
)ENGINE = SummingMergeTree()
PARTITION BY toYYYYMM(create_time)
ORDER BY (id, city)
PRIMARY KEY id;
当使用嵌套数据类型的时候:
create table summing_table_nested (
id String,
-- 在使用嵌套数据类型的时候,默认情况下,会以嵌套类型中的第一个字段作为聚合条件Key
-- 如果要使用嵌套中的复合字段作为聚合Key, 则除第一个字段外,任何名称是以Key, Id或Type为后缀结尾的字段,都将和第一个字段一起组成复合Key
nestMap Nested(
id UInt32,
Key UInt32,
val UInt64
),
create_time Datetime
)ENGINE = SummingMergeTree()
PARTITION BY toYYYYMM(create_time)
ORDER BY id;
总结:
- 用Order BY 排序键作为聚合数据的条件Key
- 只有在合并分区的时候才会触发汇总的逻辑
- 以数据分区为单位来聚合数据。当分区合并的时,同一数据分区内聚合Key相同的数据会被合并汇总,而不同分区的数据则不会被汇总。
- 如果在定义引擎的时候指定了columns汇总列(非主键的数值类型字段),则SUM汇总这些列字段,如果未指定,则聚合所有的非主键的数值类型字段。
- 在进行数据汇总的时,同一分区内,相同聚合Key的多行数据会合并成一行。其中,汇总字段会进行SUM计算;对于那些非汇总字段,则会使用第一行数据的取值。
- 支持嵌套类型结构,但列字段必须以Map后缀收尾。嵌套类型中默认以第一个字段作为聚合Key。除第一个字段外,任何名称是以Key, Id或Type为后缀结尾的字段,都将和第一个字段一起组成复合Key。
4、AggregatingMergeTree
使用时设置:ENGINE = AggregatingMergeTree() ;
在分区合并时,在每个数据分区内,会按照ORDER BY聚合。而使用何种聚合函数,以及针对那些列字段计算,则是通过定义AggregatingFunction数据类型实现的。
create table agg_table (
id String,
city String,
-- uniq 去重操作
code AggregateFunction(uniq, String),
-- sum 求和
value AggregateFunction(sum, UInt32),
create_time datetime
) engine = AggregatingMergeTree()
partition by toYYYYMM(create_time)
order by (id, city)
primary key id;
AggregateFunction是clickhouse提供的一种特殊的数据类型,能够以二进制形式存储中间状态结果。
对于AggregateFunction的列字段在写入的时候需要调用*State函数;在查询数据的时候,需要调用相应的*Merge函数,其中*表示定义时使用的聚合函数。
写入的时候需要调用uniq、sum对应的uniqState和sumState函数,并使用INSERT SELECT:
insert into table agg_table
select 'A000', 'wuhan', uniqState('code01'), sumState(toUInt32(100)), '2021-03-06 00:08:00'
查询的时候,需要调用uniqMerge, sumMerge函数:
select id, city, uniqMerge(code), sumMerge(value) from agg_table group by id, city;
AggregatingMergeTree与物化视图结合使用:
- (1)首先会有一张使用MergeTree的数据明细表作为底表,存储全量的明细数据,并对外提供实时查询。
create table agg_table_basic (
id String,
city String,
code String,
value UInt32
)ENGINE = MergeTree()
partition by city
order by (id, city);
- (2)在底表的基础上创建一张物化视图,数据写入的时候写入到底表中,数据会自动同步到物化视图,并按照AggragatingMergeTree引擎的规则处理。查询时查询的是物化视图AggragatingMergeTree。
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;
insert into table agg_table_basic values('A000', 'wuhan', 'code1', 100),
('A000', 'wuhan', 'code2', 200),
('A000', 'sichuan', 'code1', 150),
('A000', 'beijing', 'code3', 1000);
select id, city, sumMerge(value), uniqMerge(code) from agg_view group by id, city;
总结:
- 用order by 排序键作为数据聚合的条件key
- 使用AggregationFunction字段类型定义聚合函数的类型以及聚合的字段。
- 只有在分区合并的时候才会触发聚合计算的逻辑。
- 以数据分区为单位来聚合数据,当分区合并时,同一数据分区内聚合Key相同的数据会被合并计算,而不同分区的数据则不会被计算。
- 在进行数据计算时,因为分区内的数据已经按照排序见排序,所以能找到相邻且拥有相同聚合key的数据。
- 在聚合数据时,同一分区内,相同聚合key的多行数据会合并成一行。对于非主键、非AggragateFunction类型字段,则会使用第一行数据的取值。
- AggragateFunction类型的字段使用二进制存储。对于AggregateFunction的列字段在写入的时候需要调用*State函数;在查询数据的时候,需要调用相应的*Merge函数,其中*表示定义时使用的聚合函数。
- AggragatingMergeTree通常作为物化视图的引擎,与普通MergeTree搭配使用。
5、CollapsingMergeTree
CollapsingMergeTree(折叠合并树)是通过以增代删的思路,支持行级别数据修改和删除的表引擎。
通过定义一个sign标记为字段,记录数据行的状态。如果sign标记为1,则表示这是一行有效的数据;如果sign为-1,这表示这行数据需要被删除。当CollapsingMergeTree分区合并到 时候,同一数据分区内,sign标记为1和-1的一组数据会被抵消删除。
使用方法:ENGINE = CollapsingMergeTree(sign)
create table collapse_table
(
id String,
code UInt32,
create_time DateTime,
-- sign 是一个Int8类型的标志位字段
sign Int8
)ENGINE = CollapsingMergeTree(sign)
partition by toYYYYMM(create_time)
order by id; // 以此处的排序键作为判断后续数据唯一性的依据
修改一行数据的操作:
-- 修改前的插入的数据
insert into table collapse_table values('A000', 100, '2021-04-22 00:00:00', 1)
-- 在修改之前需要删除该数据
insert into table collapse_table values('A000', 100, '2021-04-22 00:00:00', -1)
-- 修改数据的操作, 直接插入修改之后的数据
insert into table collapse_table values('A000', 200, '2021-04-22 00:00:00', 1)
删除数据的操作:
-- 插入数据
insert into table collapse_table values('A000', 100, '2021-04-22 00:00:00', 1)
-- 删除数据
insert into table collapse_table values('A000', 100, '2021-04-22 00:00:00', -1)
CollapsingMergeTree在折叠数据的时候,遵循以下规则:
- 如果sign=1比sign=-1的数据多一行则保留最后一行sign=1的数据。
- 如果sign=-1比sign=1的数据多一行则保留第一行sign=-1的数据。
- 如果sign=-1和sign=1的数据行一样多,并且最后一行是sign=1,则保留第一行sign=-1和最后一行sign=1的数据。
- 如果sign=1和sign=-1的数据行一样多,并且最后一行是sign=-1,则什么也不保留。
- 其余情况Clickhouse会打印警告日志信息,但不会报错,在这种情形下,查询结果不可预知。
注意:
1.折叠数据并不是实时触发的,数据折叠只会在后台分区合并的时候发生,在分区合并之前,还是会看到旧的数据。
2.只有相同分区的数据才能被折叠
3.CollapsingMergeTree对写入数据的顺序有着严格的要求。比如先写入sign=1然后写入siign=-1的则可以被折叠,当顺序反过来则不能被折叠。
当写入的是单线程的话,则能较好控制写入顺序,当写入是多线程的时候则不能很好控制写入的顺序了。
6、VersionedCollapsngMergeTree
VersionedCollapsngMergeTree与CollapsingMergeTree作用完全相同,不同之处在于对数据的写入顺序没有要求,在同一个分区内,任意顺序的数据都能完成折叠要求。
在定义VersionedCollapsingMergeTree的时候,除了需要指定sign标记字段外,还需要指定一个Uint8类型的ver版本号字段。
create table ver_collapse_table (
id String,
code Int32,
create_time DateTime,
sign Int8,
ver Uint8
) ENGINE VersionedCollapsingMergeTree(sign, ver)
PARTITION BY toYYYYMM(create_time)
ORDER BY id;
通过自动将ver增加到ORDER BY的最后面,在每个数据分区内数据都会按照ORDER BY ${ORDER_KEY} ver DESC排序,所以写入的顺序无论是怎样的,在折叠的时候都能回到正确的顺序。
删除的时候:
insert into table collapse_table values('A000', 100, '2021-04-22 00:00:00', 1, 1)
-- 删除的时候sign=-1 版本号保持不变
insert into table collapse_table values('A000', 100, '2021-04-22 00:00:00', -1, 1)
更新的时候:
insert into table collapse_table values('A000', 100, '2021-04-22 00:00:00', -1, 1)
insert into table collapse_table values('A000', 102, '2021-04-22 00:00:00', 1, 1)
insert into table collapse_table values('A000', 103, '2021-04-22 00:00:00', 1, 2)
7、各种MergeTree之间的关系
1、继承关系
|-----ReplacingMergeTree
|
|
|
|-----SummingMergeTree
|
|
|
|-----AggragatingMergeTree
|
MergeTree-|
|
|-----CollapsingMergeTree
|
|
|
|-----VersionedCollapsingMergeTree
|
|
|
|-----GraphiteMergeTree
2、组合关系
【Replicated】 + 【Replacing, Summing, Aggregating, Collapsing, VersionedCollapsing, Graphite】 + 【MergeTree】
【Replacing, Summing, Aggregating, Collapsing, VersionedCollapsing, Graphite】 + 【MergeTree】可以组合成7种表引擎
【Replicated】 + 【Replacing, Summing, Aggregating, Collapsing, VersionedCollapsing, Graphite】 + 【MergeTree】可以组合成14种表引擎
最后
以上就是精明篮球为你收集整理的clickhouse MergeTree序列表引擎1、MergeTree2、ReplacingMergeTree3、SummingMergeTree4、AggregatingMergeTree5、CollapsingMergeTree6、VersionedCollapsngMergeTree7、各种MergeTree之间的关系的全部内容,希望文章能够帮你解决clickhouse MergeTree序列表引擎1、MergeTree2、ReplacingMergeTree3、SummingMergeTree4、AggregatingMergeTree5、CollapsingMergeTree6、VersionedCollapsngMergeTree7、各种MergeTree之间的关系所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复