ck中去重主要是借助ReplacingMeregeTree引擎,它能够在合并part的时候将主键(既排序键)相同的记录只保留一条,但是使用的过程中存在两个问题:

  1. 数据是在分区part合并的时候去重的,所以要实现全局去重,必须保证主键相同的记录在一个节点同一个分区上。
  2. ReplacingMergeTree引擎的merge是后台线程不定期触发执行的,时机是不可控的,所以并不能保证多久后不会出现重复数据,正对实时实时性高的用户不瞒住需求。

目前三种解决方案

方案一:ReplacingMergeTree+定时脚本

该方案是目前运用最广,性能最好的。是通过在明细表上出啊昂见ReplacingMergeTree引擎的物化视图,指定ORDER BY排序键作为判断重复数据的唯一键,选择数据去重的策略,默认保留最新的一条

CREATE MATERIALIZED VIEW rt_dw.dwd_cx_trd_order_base_rt_view_local
ENGINE = ReplicatedReplacingMergeTree('/clickhouse/tables/{share}/rt_dw/dwd_cx_trd_order_base_rt_view_local')
PARTITION BY toYYYYMMDD(toDateTime(create_time))
ORDER BY sub_order_id
SETTINGS index_granularity = 8192,storage_policy = 'all_disk' AS
SELECT * 
FROM rt_dw.dwd_cx_trd_order_base_rt_local

针对问题1,需要借助分布式表写入能力,可以设置根据主键哈希将数据分布到每个share上。这样就可以保证主键相同的记录在同一个节点上,既将明细表的写入策略将rand改为hash排序键。

CREATE TABLE IF NOT EXISTS rt_dw.dwd_cx_trd_order_base_rt(
	'number' Int32 COMMENT '商品数量',
	'sub_order_id' String COMMENT '子订单号',
	'total_price' Nullable(Int32) COMMENT '子订单号总价格'
) ENGIINE = Distributed(
	'chengxin02',
	'rt_dw',
	'dwd_cx_trd_order_base_rt_view_local',
	xxHash32(sub_order_id)
)

指针问题2,通过定时任务定时的触发物化视图的merge,来保证重复数据在一定的误差内
存在问题:

  1. 必须写分布式表,但是分布式表的写入性能较差,同时在集群扩容缩容的时候,share数量的变化,可能会导致当天分区主键相同的数据落到不同的节点上,部分数据不能去重
  2. 明细表必须没有数据,原因是默认的写入策略是随机的。
  3. 需要定时触发合并的表多了,会增加集群压力
  4. 指定的去重主键并非是用户后续查询过滤的条件,浪费了排序索引
方案二:ReplacingMergeTree+FINAL

该方案在高qps的环境下,性能较差。做法和方案一一致,创建RepacingMegeTree引擎的物化视图,查询的时候带上FINAL。
存在的问题:

  1. 当过滤出来的数据量大的时候,单个查询CPU消耗增大,耗时增大。
  2. 需要写分布式表,尽量让数据在后台去重,减少查询是merge的数据量
  3. 指定的去重主键并非是用户后续查询的过滤条件,浪费排序索引
  4. FINAL内存中去重不会落盘,会造成大量的重复计算
方案三:MERGETREE+argMax

argMax是ck提供的聚合函数,既argMax(field1,field2),会按照field1和field2的最大值取field1的值,既可以在查询的时候实现去重表的功能,保留某个字段值最大的一条记录。
表引擎用MergeTree就可以了,查询的时候在子查询里面去重。
该方案的优势:

  1. 不需要写分布式表,不会存在扩容等问题
  2. 建表的时候不需要指定去重主键,可以跟合理的使用排序索引
  3. 可以借助排序索引快速过滤需要的数据,只针对过滤出来的数据进行去重
select
	sum(number),
	sum(total_price)
from (
	select 
		sub_order_id,
		argMax(leader_uid,status) leader_uid,
		argMax(number,status) number,
		argMax(total_price,status) total_price
	from rt_dw.dwd_cx_trd_order_base_rt
	where leader_uid in (639312313123123,63979321731931)
	group by sub_order_id
)t;

存在问题:
未命中索引,需要group by 大量key,性能极差,cpu和耗时都很高,存在大量的重复计算

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐