写点什么

【Clickhouse】ReplaceingMergeTree 引擎 final 实现合并去重探索 | 京东云技术团队

  • 2023-06-08
    北京
  • 本文字数:3995 字

    阅读完需:约 13 分钟

【Clickhouse】ReplaceingMergeTree引擎final实现合并去重探索 | 京东云技术团队

前言

在 OLAP 实践中,在有数据更新的场景中,比如存储订单数据,我们经常会用到 ReplaceingMergeTree 引擎来去重数据,以获取数据的最新状态。但是 ReplaceingMergeTree 引擎实现数据的去重合并的操作是异步的,这样在实际查询的时候,其实是仍然有一部分数据是未进行合并的。为了保证统计数据的准确性,比如订单金额,一个常用的方法是在查询时增加 final 关键字。那 final 关键字是如何合并数据的,以及合并的数据范围是怎样的,本文就对此做一个简单的探索。


知识准备


分片:分片就是 clickhouse 的实例节点,不同的分片就代表不同的节点或机器,分片之间是物理隔离的 分区:分区是一个表中通过指定的规则划分而成的逻辑数据集,比如日期分区,分区是一种逻辑上的,不同的分片上会有相同的分区

探索过程

探索过程比较长,请大家保持耐心,如果不想看过程,可以直接看结论哈,马上开始~


本文基于的 clickhouse 版本为 version 23.3.1.2823

创建表

创建 ReplacingMergeTree 引擎的表,分布式表 union_order_onl_all_test,本地表 union_order_onl_local_test,以日期为分区,order_id 作为排序键,mid 是消息 ID,用消息 ID 作为数据变更的版本号,同时 order_id 字段作为分片 hash 字段,不同的订单会被写入到不同的实例上。


CREATE TABLE gbn_onl_mix.union_order_onl_local_test on cluster lf6ckcnts05(    `order_id` UInt64 COMMENT '订单号',    `after_prefr_amount_1` Float64 COMMENT '订单金额',    `deal_flag` UInt8 COMMENT '成交标识',    `mid` String COMMENT '消息ID',    `update_time` String COMMENT '更新时间',    `ver` UInt64 DEFAULT toUInt64OrZero(mid) COMMENT '版本号',    `dt`Date DEFAULT toDate(update_time) COMMENT '分区')ENGINE = ReplicatedReplacingMergeTree('/clickhouse/lf6ckcnts05/jdob_ha/gbn_onl_mix/lf6ckcnts05/{shard}', '{replica}', ver)PARTITION BY toYYYYMMDD(dt)ORDER BY (order_id)TTL dt + toIntervalDay(7)SETTINGS storage_policy = 'jdob_ha', index_granularity = 3

CREATE TABLE gbn_onl_mix.union_order_onl_all_test on cluster lf6ckcnts05 as gbn_onl_mix.union_order_onl_local_testengine=Distributed(lf6ckcnts05, gbn_onl_mix, union_order_onl_local_test, cityHash64(order_id)) ;
复制代码

数据初始化

初始数据包括 2 个订单,111 和 222,初始版本都是 0,初始成交状态也都是 0,日期是 2023-05-28


INSERT into gbn_onl_mix.union_order_onl_all_test (order_id,after_prefr_amount_1,deal_flag,mid,update_time) values ('111',1,0, 0,'2023-05-28'),('222',2,0,0,'2023-05-28');
复制代码


查询分区信息和数据如下:可以看到数据被写入到了 1 个分区的 2 个 part 中,分区都是 20230528,part 名都是 20230528_0_0_0


知识点详见 https://clickhouse.com/docs/zh/engines/table-engines/mergetree-family/custom-partitioning-key 分区信息有重复是因为 lf6ckcnts05 集群的配置是有一个副本




验证同分片同分区数据合并

final 合并

order_id=111 有数据更新,mid 变成了 1,即插入如下数据


INSERT into gbn_onl_mix.union_order_onl_all_test (order_id,after_prefr_amount_1,deal_flag,mid,update_time) values ('111',1,0, 1,'2023-05-28');
复制代码


查询分区信息如下,可见增加了一个 part,分区为 20230528,part 名为 20230528_1_1_0



查询数据如下,可见 order_id=111 的订单,版本 0 和版本 1 的数据都是存在的


SELECT * FROM gbn_onl_mix.union_order_onl_all_test WHERE dt = '2023-05-28'
复制代码



查询数据使用 final 结果如下,可见 order_id=111 的订单,只查询出最新版本 1 的数据


SELECT * FROM gbn_onl_mix.union_order_onl_all_test final WHERE dt = '2023-05-28'
复制代码



再查询一下实际的数据如下,结果 order_id=111 的 2 个版本的数据还是都被查询出来了,可见 final 查询对实际物理数据的存储没有影响


SELECT * FROM gbn_onl_mix.union_order_onl_all_test WHERE dt = '2023-05-28'
复制代码



小结:final 可以合并同分片同分区的数据,并且 final 合并数据只是针对当次查询,不会对数据进行物理合并

引擎合并

order_id=111 有数据更新,mid 变成了 2,即插入如下数据


INSERT into gbn_onl_mix.union_order_onl_all_test (order_id,after_prefr_amount_1,deal_flag,mid,update_time) values ('111',1,0, 2,'2023-05-28');
复制代码


查询分区和数据如下,分区 20230528,增加一个 part,名为 20230528_2_2_0




order_id=111 有数据更新,mid 变成了 3,即插入如下数据


INSERT into gbn_onl_mix.union_order_onl_all_test (order_id,after_prefr_amount_1,deal_flag,mid,update_time) values ('111',1,0, 3,'2023-05-28');
复制代码


分区 20230528,增加名为 20230528_2_2_0 的 part



此时数据还没有被引擎合并,先去吃个饭吧~


Later For a Moment ~~~


吃饭回来,查询分区,发现数据已经被引擎合并了,合并后的分区为 20230528_0_3_1,但是同分区不同分片的数据没有被合并



小结:ReplaceingMergeTree 引擎合并数据,合并的是同分片同分区的数据

验证同分片不同分区数据合并

final 合并

order_id=111 数据继续更新,mid 变成了 4,即插入如下数据


INSERT into gbn_onl_mix.union_order_onl_all_test (order_id,after_prefr_amount_1,deal_flag,mid,update_time) values ('111',1,1, 4,'2023-05-29');
复制代码


查询分区和数据如下,可见增加了一个 part,分区是 20230529,part 名为 20230529_0_0_0,order_id=111 订单数据版本 3 和版本 4 同时存储,数据还未合并




使用 final 查询数据,结果如下,我们会发现,order_id=111 的订单在 2 个分区 2023-05-28 和 2023-05-29 中的数据被合并了


SELECT * FROM gbn_onl_mix.union_order_onl_all_test final
复制代码



小结:final 可以跨分区进行合并

引擎合并

order_id=111 数据继续更新,mid 变成 5、6、7,即插入如下数据


INSERT into gbn_onl_mix.union_order_onl_all_test (order_id,after_prefr_amount_1,deal_flag,mid,update_time) values ('111',1,1, 5,'2023-05-29','111',1,1, 6,'2023-05-29','111',1,1, 7,'2023-05-29');
复制代码


查询分区和数据如下,可见增加 part 20230529_1_1_0,只插入了一条最新消息为 7 的数据,即插入数据时,数据就已经合并了




order_id=111 数据继续更新,mid 变成 8、9,即插入如下数据


INSERT into gbn_onl_mix.union_order_onl_all_test (order_id,after_prefr_amount_1,deal_flag,mid,update_time) values ('111',1,1, 8,'2023-05-29');INSERT into gbn_onl_mix.union_order_onl_all_test (order_id,after_prefr_amount_1,deal_flag,mid,update_time) values ('111',1,1, 9,'2023-05-29');
复制代码


查询分区和数据如下,新增 part 20230529_2_2_0 和 20230529_3_3_0




使用 final 同时查询 2 个分区数据,以及单独查询单个分区的数据,结果如下,可以看到卡不同的分区,最后合并的结果也不同,(这不是废话嘛~~)


SELECT * FROM gbn_onl_mix.union_order_onl_all_test final
复制代码



SELECT * FROM gbn_onl_mix.union_order_onl_all_test final WHERE dt = '2023-05-29'
复制代码



SELECT * FROM gbn_onl_mix.union_order_onl_all_test final WHERE dt = '2023-05-28'
复制代码



Later For a Moment ~~~


数据合并完成,结果如下,part 20230529_0_0_0、20230529_1_1_0、20230529_2_2_0、20230529_3_3_0 变成 active=0,合并后 part 为 20230529_0_3_1,但是分区 20230508 的 part 20230528_0_3_1 并没有被合并



查询分区数据,结果如下


SELECT * FROM gbn_onl_mix.union_order_onl_all_test WHERE dt = '2023-05-28'SELECT * FROM gbn_onl_mix.union_order_onl_all_test WHERE dt = '2023-05-29'
复制代码




小结:无论是从分区信息还是从数据结果来看,ReplaceingMergeTree 引擎是不会合并同分片不同分区的数据的

验证不同分片数据合并

final 合并

考虑 order_id=222 的订单数据,金额修改成 22 以做区分,在不同的分片上插入变更数据,本次插入改用向本地表中插入数据,可达到跨分片实例的效果,如下


order_id=222 的订单,mid 变成 1,即插入如下数据


INSERT into gbn_onl_mix.union_order_onl_local_test (order_id,after_prefr_amount_1,deal_flag,mid,update_time) values ('222',22,0,1,'2023-05-28');
复制代码


查询数据,发现居然和版本 0 插入到同一个分片上了


SELECT * FROM gbn_onl_mix.union_order_onl_local_test WHERE dt = '2023-05-28'
复制代码



再来一次,order_id=222 的订单,mid 变成 2,即插入如下数据


INSERT into gbn_onl_mix.union_order_onl_local_test (order_id,after_prefr_amount_1,deal_flag,mid,update_time) values ('222',22,0,2,'2023-05-28');
复制代码


查询数据,可见这次数据是插入到了不同的分片实例上


SELECT * FROM gbn_onl_mix.union_order_onl_local_test WHERE dt = '2023-05-28'
复制代码



查看目前分区 20230528 的数据,如下


SELECT * FROM gbn_onl_mix.union_order_onl_all_test WHERE dt = '2023-05-28'
复制代码



使用 final 查询结果如下,可见 final 查询不能合并跨分片的数据,(order_id=222,ver=1 和 ver=2 是存储在不同分片上的数据)


SELECT * FROM gbn_onl_mix.union_order_onl_all_test final WHERE dt = '2023-05-28'
复制代码



引擎合并

手动触发引擎合并,如下


optimize table union_order_onl_local_test on cluster lf6ckcnts05 FINAL;
复制代码


查询数据结果,如下,结果同 final 查询



小结:无论是 final 查询还是引擎合并,不同分片上的数据都不会被合并,即使是同分区的也不会被合并

结论

啰哩啰嗦这么多,总结一下吧~~


1.对于不同分片上的数据来说,ReplaceingMergeTree 引擎合并和查询时加 final 的合并,都不会合并不同分片上的数据


2.对于相同分片上的数据来说,ReplaceingMergeTree 引擎合并,只合并同分区的数据,不同分区的数据不会合并;查询时加 final 的合并,会对不同分区的数据进行合并,合并是按照排序键进行合并的,如果想避免不同分区间的合并可以在排序键中增加分区字段


如有问题请指正,欢迎大家沟通交流,感谢~~


作者:京东零售 曹建奇

来源:京东云开发者社区

发布于: 刚刚阅读数: 2
用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
【Clickhouse】ReplaceingMergeTree引擎final实现合并去重探索 | 京东云技术团队_OLAP_京东科技开发者_InfoQ写作社区