【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_test
engine=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的合并,会对不同分区的数据进行合并,合并是按照排序键进行合并的,如果想避免不同分区间的合并可以在排序键中增加分区字段
如有问题请指正,欢迎大家沟通交流,感谢~~
作者:京东零售 曹建奇
来源:京东云开发者社区
【Clickhouse】ReplaceingMergeTree引擎final实现合并去重探索的更多相关文章
- Clickhouse表引擎探究-ReplacingMergeTree
作者:耿宏宇 1 表引擎简述 1.1 官方描述 MergeTree 系列的引擎被设计用于插入极大量的数据到一张表当中.数据可以以数据片段的形式一个接着一个的快速写入,数据片段在后台按照一定的规则进行合 ...
- Clickhouse表引擎之MergeTree
1.概述 在Clickhouse中有多种表引擎,不同的表引擎拥有不同的功能,它直接决定了数据如何读写.是否能够并发读写.是否支持索引.数据是否可备份等等.本篇博客笔者将为大家介绍Clickhouse中 ...
- ClickHouse MergeTree引擎
Clickhouse 中最强大的表引擎当属 MergeTree (合并树)引擎及该系列(*MergeTree)中的其他引擎. MergeTree 系列的引擎被设计用于插入极大量的数据到一张表当中.数据 ...
- PHP数组合并+与array_merge的区别分析 & 对多个数组合并去重技巧
PHP中两个数组合并可以使用+或者array_merge,但之间还是有区别的,而且这些区别如果了解不清楚项目中会要命的! 主要区别是两个或者多个数组中如果出现相同键名,键名分为字符串或者数字,需要注意 ...
- 两个List合并去重
今天遇到一个合并去重问题,从网上搜索一样总结出来两个比较简单的方法,这里去重是只能取出地址相同的数据,例如:如果两个字符串的值相同但都是单独new出来的这样去不了 @Test public void ...
- linux shell文件合并 去重 分割
1,合并+去重+分割 转载:shell 文件合并,去重,分割 - kakaisgood - 博客园 (cnblogs.com) 第一:两个文件的交集,并集前提条件:每个文件中不得有重复行1. 取出两个 ...
- clickhouse核心引擎MergeTree子引擎
在clickhouse使用过程中,针对数据量和查询场景,MergeTree是最常用也是较为合适的表引擎.针对特定的业务,MergeTree的子引擎可以针对不同的业务而定,但都基于MergeTree引擎 ...
- UniqueMergeTree:支持实时更新删除的 ClickHouse 表引擎
UniqueMergeTree 开发的业务背景 首先,我们看一下哪些场景需要用到实时更新. 我们总结了三类场景: 第一类是业务需要对它的交易类数据进行实时分析,需要把数据流同步到 ClickHouse ...
- clickhouse在风控-风险洞察领域的探索与实践
一.风险洞察平台介绍 以Clickhouse+Flink实时计算+智能算法为核心架构搭建的风险洞察平台, 建立了全面的.多层次的.立体的风险业务监控体系,已支撑欺诈风险.信用风险.企业风险.小微风险. ...
- ClickHouse(07)ClickHouse数据库引擎解析
目录 Atomic 建表语句 特性 Table UUID RENAME TABLES DROP/DETACH TABLES EXCHANGE TABLES ReplicatedMergeTree in ...
随机推荐
- 最新版本 Stable Diffusion 开源 AI 绘画工具之汉化篇
目录 汉化预览 下载汉化插件一 下载汉化插件二 下载汉化插件三 开启汉化 汉化预览 在上一篇文章中,我们安装好了 Stable Diffusion 开源 AI 绘画工具 但是整个页面都是英文版的,对于 ...
- Java基础关于栈和堆的内存分配问题(转载)
AVA在程序运行时,在内存中划分5片空间进行数据的存储.分别是:1:寄存器.2:本地方法区.3:方法区.4:栈.5:堆. 基本,栈stack和堆heap这两个概念很重要,不了解清楚,后面就不用学了. ...
- AI开发实践:关于停车场中车辆识别与跟踪
摘要:本案例我们使用FairMOT进行车辆检测与跟踪.yolov5进行车牌检测.crnn进行车牌识别,在停车场入口.出口.停车位对车辆进行跟踪与车牌识别,无论停车场路线多复杂,小车在你掌控之中! 本文 ...
- 搭建Hadoop2.7.2和Hive2.3.3以及Spark3.1.2
Hadoop 简介 Hadoop是一个用Java编写的Apache开源框架,允许使用简单的编程模型跨计算机集群分布式处理大型数据集.Hadoop框架工作的应用程序在跨计算机集群提供分布式存储和计算的环 ...
- Vulnhub Bravery靶机 Walkthrough
Bravery Recon 使用netdiscover对本地网络进行arp扫描. ┌──(kali㉿kali)-[~] └─$ sudo netdiscover -r 192.168.80.0/24 ...
- stable diffusion打造自己专属的LORA模型
通过Lora小模型可以控制很多特定场景的内容生成. 但是那些模型是别人训练好的,你肯定很好奇,我也想训练一个自己的专属模型(也叫炼丹-_-). 甚至可以训练一个专属家庭版的模型(family mode ...
- 碉堡!“万物皆可分”标记模型上线「GitHub 热点速览」
这周有个让人眼前一亮的图像识别模型 segment-anything,它能精细地框出所有可见物体,它标记出的物体边界线清晰可见.如此出色的模型,自然获得了不少人的赞赏,开源没几天,就拿下了 18k+ ...
- Java设计模式 —— 装饰模式
12 装饰模式 12.1 装饰模式概述 Decorator Pattern: 动态地给一个对象增加一些额外的职责.提供一种比使用子类更加灵活的方案来扩展功能. 装饰模式是一种用于替代继承的技术,通过一 ...
- golang常用库包:log日志记录-uber的Go日志库zap使用详解
Go 日志记录库:uber-go 的日志操作库 zap 使用 一.简介 zap 是 uber 开源的一个高性能,结构化,分级记录的日志记录包. go1.20.2 zap v1.24.0 zap的特性 ...
- 新手如何让一个python写的游戏运行起来
本文主要解决问题为python中的pygame库安装 安装包版本:python-3.4.3.amd64.msi 下载链接:https://pan.baidu.com/s/1_jIRdVugSNzXKb ...