clickHouse的ReplacingMergeTree存储引擎使用
一、ReplacingMergeTree作用
ClickHouse中最常用也是最基础的表引擎为MergeTree,在它的功能基础上添加特定功能就构成了MergeTree系列引擎。MergeTree支持主键,但主键主要用来缩小查询范围,且不具备唯一性约束,可以正常写入相同主键的数据。但在一些情况下,可能需要表中没有主键重复的数据。ReplacingMergeTree就是在MergeTree的基础上加入了去重的功能,但它仅会在合并分区时,去删除重复的数据,写入相同数据时并不会引发异常。(主要应用于记录分区内记录唯一时刻状态,用于分析用户系统较多【例如用户某日最后登录时间】)
二、功能示例
创建一张ReplacingMergeTree的表和创建MergeTree类似,修改引擎即可。ReplacingMergeTree引擎创建规范为:ENGINE = ReplacingMergeTree([ver]),其中ver为选填参数,它需要指定一个UInt8/UInt16、Date或DateTime类型的字段,它决定了数据去重时所用的算法,如果没有设置该参数,合并时保留分组内的最后一条数据;如果指定了该参数,则保留ver字段取值最大的那一行。
1、不指定ver参数
-- 创建未指定ver参数ReplacintMergeTree引擎的表 CREATE TABLE replac_merge_test ( `id` String, `code` String, `create_time` DateTime ) ENGINE = ReplacingMergeTree() PARTITION BY toYYYYMM(create_time) PRIMARY KEY id ORDER BY (id, code)
ReplacingMergeTree会根据ORDER BY所声明的表达式去重
-- 在上述表中插入数据 insert into replac_merge_test values ('A000', 'code1', now()),('A000', 'code1', now()), ('A001', 'code1', now()), ('A001', 'code2', now()), ('A0002', 'code2', now());
-- 查询当前数据 select * from replac_merge_test;
┌─id────┬─code──┬─────────create_time─┐
│ A000 │ code1 │ 2020-07-28 21:23:48 │
│ A000 │ code1 │ 2020-07-28 21:30:00 │
│ A0002 │ code2 │ 2020-07-28 21:23:48│
│ A001 │ code1 │ 2020-07-28 21:23:48 │
│ A001 │ code2 │ 2020-07-28 21:30:00 │
└───────┴───────┴─────────────────────┘
-- 强制进行分区合并 optimize table replac_merge_test FINAL;
-- 再次查询数据 select * from replac_merge_test;
┌─id────┬─code──┬─────────create_time─┐
│ A000 │ code1 │ 2020-07-28 21:30:00 │
│ A0002 │ code2 │ 2020-07-28 21:23:48│
│ A001 │ code1 │ 2020-07-28 21:23:48 │
│ A001 │ code2 │ 2020-07-28 21:30:00 │
└───────┴───────┴─────────────────────┘
通过上面示例可以看到,id、code相同的字段’A000’,'code1’被去重剩余一条数据,由于创建表时没有设置ver参数,故保留分组内的最后一条数据(create_time字段)
-- 再次使用insert插入一条数据 insert into replac_merge_test values ('A001', 'code1', '2020-07-28 21:30:00'); -- 查询表中数据 select * from replac_merge_test; ┌─id────┬─code──┬─────────create_time─┐ │ A000 │ code1 │ 2020-07-28 21:30:00 │ │ A0002 │ code2 │ 2020-07-28 21:23:48 │ │ A001 │ code1 │ 2020-07-28 21:23:48 │ │ A001 │ code2 │ 2020-07-28 21:30:00 │ └───────┴───────┴─────────────────────┘ ┌─id───┬─code──┬─────────create_time─┐ │ A001 │ code1 │ 2020-07-28 21:30:00 │ └──────┴───────┴─────────────────────┘
可以看到,再次插入重复数据时,查询仍然会存在重复。在ClickHouse中,默认一条insert插入的数据为同一个数据分区,不同insert插入的数据为不同的分区,所以ReplacingMergeTree是以分区为单位进行去重的,也就是说只有在相同的数据分区内,重复数据才可以被删除掉。只有数据合并完成后,才可以使用引擎特性进行去重。
2、指定ver参数
-- 创建指定ver参数ReplacingMergeTree引擎的表 CREATE TABLE replac_merge_ver_test ( `id` String, `code` String, `create_time` DateTime ) ENGINE = ReplacingMergeTree(create_time) PARTITION BY toYYYYMM(create_time) PRIMARY KEY id ORDER BY (id, code) -- 插入测试数据 insert into replac_merge_ver_test values('A000', 'code1', '2020-07-10 21:35:30'),('A000', 'code1', '2020-07-15 21:35:30'),('A000', 'code1', '2020-07-05 21:35:30'),('A000', 'code1', '2020-06-05 21:35:30'); -- 查询数据 select * from replac_merge_ver_test; ┌─id───┬─code──┬─────────create_time─┐ │ A000 │ code1 │ 2020-06-05 21:35:30 │ └──────┴───────┴─────────────────────┘ ┌─id───┬─code──┬─────────create_time─┐ │ A000 │ code1 │ 2020-07-10 21:35:30 │ │ A000 │ code1 │ 2020-07-15 21:35:30 │ │ A000 │ code1 │ 2020-07-05 21:35:30 │ └──────┴───────┴─────────────────────┘ -- 强制进行分区合并 optimize table replac_merge_ver_test FINAL; -- 查询数据 select * from replac_merge_ver_test; ┌─id───┬─code──┬─────────create_time─┐ │ A000 │ code1 │ 2020-07-15 21:35:30 │ └──────┴───────┴─────────────────────┘ ┌─id───┬─code──┬─────────create_time─┐ │ A000 │ code1 │ 2020-06-05 21:35:30 │ └──────┴───────┴─────────────────────┘
由于上述创建表是以create_time的年月来进行分区的,可以看出不同的数据分区,ReplacingMergeTree并不会进行去重,并且在相同数据分区内,指定ver参数后,会保留同一组数据内create_time时间最大的那一行数据。
三、ReplacingMergeTree引擎总结
①使用ORDER BY排序键,作为判断数据是否重复的唯一键
②只有在合并分区时,才会触发数据的去重逻辑
③删除重复数据,是以数据分区为单位。同一个数据分区的重复数据才会被删除,不同数据分区的重复数据仍会保留
④在进行数据去重时,由于已经基于ORDER BY排序,所以可以找到相邻的重复数据
故数据去重策略为:
1)若指定了ver参数,则会保留重复数据中,ver字段最大的那一行
2)若未指定ver参数,则会保留重复数据中最末的那一行数据