MergeTree引擎的表,由于重复入库导致了表中数据重复,需要将重复的数据删除,只保留一条记录。
在使用Hive的时候,遇到这种情况通常是使用row_number
取第一条插入到临时表中,然后将原表数据删除,再将临时表数据写回来就可以实现去重。
但是CK(clickhouse)中不支持row_number
函数,需要使用别的方法去重。
翻阅文档后,发现可以使用ReplacingMergeTree + Optimize来手动实现去重。需要注意的是这种方式不太适合于超大数据量数据的去重。
先介绍下ReplacingMergeTree和Optimize,具体的去重案例在最后。
ReplacingMergeTree也是MergeTree派系的,与MergeTree的不用点在于它会删除排序键值相同的重复项。
数据的去重只会在数据分区合并期间进行,合并会在后台一个不确定的时间进行。
可以调用 OPTIMIZE
语句手动触发的合并,要注意的是OPTIMIZE
语句会引发对数据的大量读写。
因此,ReplacingMergeTree
适用于在后台清除重复的数据以节省空间,但是它不保证没有重复的数据出现。
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = ReplacingMergeTree([ver])
[PARTITION BY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]
ReplacingMergeTree的参数:
ver
— 版本列。类型为 UInt*
, Date
或 DateTime
。可选参数。
在数据合并的时候,ReplacingMergeTree
从所有具有相同排序键的行中选择一行留下:
ver
列未指定,保留最后一条。ver
列已指定,保留 ver
值最大的版本。从上面我们可以看出,将要去重数据中的唯一标识字段设置为排序字段,然后可选的指定版本列,ReplacingMergeTree就可以完成数据的去重了。
另外要注意的是,要去重的重复数据必须位于同一个分区中。
Optimize用于手动触发MergeTree引擎的分区合并。
语法:
OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | PARTITION ID 'partition_id'] [FINAL] [DEDUPLICATE]
Optimize有以下几点要注意的:
默认是异步执行的。
如果 OPTIMIZE
出于任何原因不执行合并,它会报错。 要启用通知,需要使用 optimize_throw_if_noop 设置。
可以指定分区,来触发对应分区的合并。如果指定了PARTITION
,仅合并指定的分区。
如果您指定 FINAL
,即使所有数据已经在一个part中,也会执行优化。如果不加final,数据已经在同一个分区中,Optimize就不生效。
如果您指定 DEDUPLICATE
,则将对完全相同的行进行重复数据删除(所有列进行比较),这仅适用于MergeTree引擎。
CREATE TABLE raw_log
(
`event_time` DateTime COMMENT '时间',
`uuid` String COMMENT '唯一标识',
`message` String COMMENT '日志内容'
)
ENGINE = MergeTree()
PARTITION BY toStartOfHour(event_time)
ORDER BY uuid;
需要对uuid重复的数据进行去重删除
CREATE TABLE replace_log
(
`event_time` DateTime COMMENT '时间',
`uuid` String COMMENT '唯一标识',
`message` String COMMENT '日志内容'
)
ENGINE = ReplacingMergeTree()
PARTITION BY toStartOfHour(event_time)
ORDER BY uuid;
-- 将数据导入到ReplacingMergeTree表
insert into replace_log select * from raw_log;
-- 触发合并,进行去重
OPTIMIZE TABLE replace_log final;
-- 删除原表的数据
alter table raw_log delete where 1=1;
-- 将ReplacingMergeTree表合并后的数据写回原表
insert into raw_log select * from replace_log;
以上就实现了重复数据只保留一条的效果。
optimize文档:https://clickhouse.tech/docs/zh/sql-reference/statements/misc/#misc_operations-optimize
ReplacingMergeTree文档:https://clickhouse.tech/docs/zh/engines/table-engines/mergetree-family/replacingmergetree/