Clickhouse表引擎探究-ReplacingMergeTree

news2024/10/5 12:47:48

作者:耿宏宇

1 表引擎简述

1.1 官方描述

MergeTree 系列的引擎被设计用于插入极大量的数据到一张表当中。数据可以以数据片段的形式一个接着一个的快速写入,数据片段在后台按照一定的规则进行合并。相比在插入时不断修改(重写)已存储的数据,这种策略会高效很多。

ReplacingMergeTree 引擎和 MergeTree 的不同之处在于它会删除排序键值相同的重复项。
数据的去重只会在数据合并期间进行。合并会在后台一个不确定的时间进行,因此你无法预先作出计划。有一些数据可能仍未被处理。尽管你可以调用 OPTIMIZE 语句发起计划外的合并,但请不要依靠它,因为 OPTIMIZE 语句会引发对数据的大量读写。

1.2 本地表语法

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]
[PRIMARY KEY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...]
[SETTINGS name=value, ...]

参数介绍

  • ver — 版本列。类型为 UInt*, Date 或 DateTime。可选参数。
    在数据合并的时候,ReplacingMergeTree 从所有具有相同排序键的行中选择一行留下:
    1.如果 ver 列未指定,保留最后一条。
    2.如果 ver 列已指定,保留 ver 值最大的版本。
  • PRIMARY KEY expr 主键。如果要 选择与排序键不同的主键,在这里指定,可选项。
    默认情况下主键跟排序键(由 ORDER BY 子句指定)相同。 因此,大部分情况下不需要再专门指定一个 PRIMARY KEY 子句。
  • SAMPLE BY EXPR 用于抽样的表达式,可选项
  • PARTITION BY expr 分区键
  • ORDER BY expr 排序键

    1.3 分区表语法

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = Distributed(cluster, database, table[, sharding_key[, policy_name]])
[SETTINGS name=value, ...]

参数介绍

  • cluster 集群名
  • table 远程数据表名
  • sharding_key 分片规则
  • policy_name 规则名,它会被用作存储临时文件以便异步发送数据

2 键的概念

Clickhouse的部署,分为单机模式和集群模式,还可以开启副本。两种模式,数据表在创建语法、创建步骤和后续的使用方式上,存在一定的差异。

在定义表结构时,需要指定不同的键,作用如下。

分片:所有分片节点的权重加和得到S,可以理解为sharing动作取模的依据,权重X=W/S。分片键 Mod S 得到的值,与哪个分片节点匹配,则会写入哪个分片。不同分片可能存在于不同的集群节点,即便不同分片在同一节点,但ck在merge时,维度是同一分区+同一分片,这是物理文件的合并范围。
如果我们权重分别设置为1,2,3 那么总权重是6,那么总区间就是[0,6),排在shard配置第一位的node01,权重占比为1/6,所以属于区间[0,1),排在shard配置第二位的node02,占比2/6,所以区间为[1,3),至于最后的node03就是[3,6).所以如果rand()产生的数字除以6取余落在哪个区间,数据就会分发到哪个shard,通过权重配置,可以实现数据按照想要的比重分配.

3 分片的作用

3.1 分片规则

在分布式模式下,ClickHouse会将数据分为多个分片,并且分布到不同节点上。不同的分片策略在应对不同的SQL Pattern时,各有优势。ClickHouse提供了丰富的- - - sharding策略,让业务可以根据实际需求选用。

  • random随机分片:写入数据会被随机分发到分布式集群中的某个节点上。
  • constant固定分片:写入数据会被分发到固定一个节点上。
  • column value分片:按照某一列的值进行hash分片。
  • 自定义表达式分片:指定任意合法表达式,根据表达式被计算后的值进行hash分片。

3.2 类比

以MySQL的分库分表场景为例:

  • 2个库,1个表分4个子表,采用一主一从模式。
  • db01包含tab-1和tab-2,db-2包含tab-3和tab-4;
  • 在配置sharding规则时,需要设置分库规则、分表规则;
    一条记录写入时,会计算它要写入哪个表、哪个库,写入的记录会被从节点复制。

这个MySQL的例子,与CK的分区+分片+副本在逻辑上基本一致。分区理解为数据写入哪个表,分片可以理解为数据写入哪个库,副本则是从节点的拷贝。

3.3 分片、分区与副本

Clickhouse分片是集群模式下的概念,可以类比MySQL的Sharding逻辑,副本是为了解决Sharing方案下的高可用场景所存在的。
下图描述了一张Merge表的各类键的关系,也能反映出一条记录的写入过程。

4 数据合并限制

理清了分区与分片的概念,也就明白CK的数据合并,为什么要限制相同分区、相同分片,因为它们影响数据的存储位置,merge操作只能针对相同物理位置(分区目录)的数据进行操作,而分片会影响数据存储在哪个节点上。
一句话,使用CK的ReplacingMergeTree引擎的去重特性,期望去重的数据,必须满足拥有 相同排序键、同一分区、同一分片。
接下来针对这一要求,在数据上进行验证。

5 数据验证

5.1 场景设置

这里是要验证上面的结论,“期望去重的数据,必须满足在相同排序键、同一分区、同一分片”;
首先拥有相同排序键才会在merge操作时进行判断为重复,因此保证测试数据的排序键相同;剩余待测试场景则是分区与分片。
由此进行场景设置:

  • 相同记录,能够写入同一分区、同一分片
  • 相同记录,能够写入同一分区,不同分片
  • 相同记录,能够写入不同分区,不同分片
  • 相同记录,能够写入不同分区、相同分片
    再叠加同步写入方式:
  • 直接写本地表
  • 直接写分布式表
    补充:分区键与分片键,是否必须相同?

5.2 第一天测试

场景1: 相同记录,能够写入同一分区、同一分片

一次执行3条插入,插入本地表
[main_id=101,sku_id=SKU0002;barnd_code=BC01,BC02,BC03]
select * from test_ps.sku_detail_same_partition_same_shard_all;

分三次执行,插入本地表
[main_id=101,sku_id=SKU0001;barnd_code=BC01,BC02,BC03]
select * from test_ps.sku_detail_same_partition_same_shard_all;

分三次执行,插入分布式表
[main_id=101,sku_id=SKU0001;barnd_code=BC001,BC002,BC003]
select * from test_ps.sku_detail_same_partition_same_shard_all;

select * from test_ps.sku_detail_same_partition_same_shard_all final;

结论1
1.采用分布式表插入数据,保证分片键、分区键的值相同,才能保证merge去重成功
排除本地表插入场景
2.采用本地表插入数据,在分片键、分区键相同的情况下,无法保证merge去重

  • 在一个session(一次提交)里面包含多个记录,直接会得到一条记录,插入过程去重
    在第一次insert时,准备的3条insert语句是一次执行的,查询后只有1条记录。
  • 在多个session(多次提交)记录,不会直接去重,但有可能写到不同集群节点,导致无法去重
    分3次执行3条insert语句,查询后有3条记录,且通过final查询后有2条记录,合并去重的那2条记录是写入在同一集群节点。【参考SKU0002的执行结果】

后面直接验证插入分布式表场景。

场景2:相同记录,能够写入同一分区,不同分片

  • 分片键采用的rand()方式,随机生成。

分三次执行,插入分布式表
[main_id=103,sku_id=SKU0003;barnd_code=BC301,BC302,BC303]
检查数据插入状态
select * from test_ps.sku_detail_same_partition_diff_shard_all where main_id =103 ;

检查merge的去重结果
select * from test_ps.sku_detail_same_partition_diff_shard_all final where main_id =103 ;

分五次执行,插入分布式表
[main_id=104,sku_id=SKU0004;barnd_code=BC401,BC402,BC403,BC404,BC405]
检查数据插入状态
select * from test_ps.sku_detail_same_partition_diff_shard_all where main_id =104 ;

检查merge的去重结果
select * from test_ps.sku_detail_same_partition_diff_shard_all final where main_id =104 ;

结论2

采用分布式表插入数据,保证分区键的值相同、分片键的值随机,无法保证merge去重

  • 如果插入记录时,通过rand()生成的数字取模后的值一样,很幸运最终可以merge去重成功
  • 如果插入记录时,通过rand()生成的数字取模后的值不一样,最终无法通过merge去重

场景3:相同记录,能够写入不同分区,不同分片

  • 分片键采用的rand()方式,随机生成;
  • 分区键为了方便测试,采用创建时间。

分五次执行,插入分布式表
[main_id=105,sku_id=SKU0005;barnd_code=BC501,BC502,BC503,BC504,BC505]

检查数据插入状态
select * from test_ps.sku_detail_diff_partition_diff_shard_all where main_id =105 ;

检查merge的去重结果
select * from test_ps.sku_detail_diff_partition_diff_shard_all final where main_id =105;

结论3
采用分布式表插入数据,分区键的值与排序键不一致、分片键的值随机,无法保证merge去重

  • 按当前测试结果,虽然create_time都不相同,也就是分区不同,也发生了数据合并
  • 数据发生合并,但结果并不是完全按排序键进行合并的

场景4:相同记录,能够写入 不同分区、相同分片

  • 分片键采用main_id;
  • 分区键为了方便测试,采用创建时间。

分六次执行,插入分布式表
[main_id=106,sku_id=SKU0006;barnd_code=BC601,BC602,BC603,BC604,BC605,BC606]

检查数据插入状态
select * from test_ps.sku_detail_diff_partition_same_shard_all where main_id =106 ;

检查merge的去重结果
select * from test_ps.sku_detail_diff_partition_same_shard_all final where main_id =106;

此场景,经过第二天检索,数据并没有进行merge,而是用final关键字依然能检索出去重后的结果。也就是说final关键字只是在内存中进行去重,由于所在分区不同,文件是没有进行merge合并的,也就没有去重。反观相同分区、相同分片的数据表,数据已经完成了merge合并,普通检索只能得到一条记录。

结论4
采用分布式表插入数据,分区键的值与排序键不一致、分片键的值固定,无法实现merge去重

5.3 第二天检查

以下均采用普通查询,发现如下情况

  • 分片不同的表,其数据没有合并
  • 分片相同、分区不同的没有合并
  • 分片相同、分区相同的已经完成了合并

select * from test_ps.sku_detail_same_partition_same_shard_all;

select * from test_ps.sku_detail_same_partition_diff_shard_all;

select * from test_ps.sku_detail_diff_partition_diff_shard_all;

select * from test_ps.sku_detail_diff_partition_same_shard_all;

6 总结

根据测试结果,在不同场景下的合并情况:

  • 如果数据存在在相同分片,且相同分区,绝对可以实现合并去重。
  • 如果数据存储在不同分片,不同分区,将不会进行合并去重。
  • 如果数据存储在不同分片,但同一分片内保证在相同分区,会进行此分片下的merge去重。
  • 如果数据存在在相同分片,但不同分区,不会进行merge去重,但通过final关键字可以在CK内存中对相同分区、相同分片的数据进行去重。

在Clickhouse的ReplacingMergeTree进行merge操作时,是根据排序键(order by)来识别是否重复、是否需要合并。而分区和分片,影响的是数据的存储位置,在哪个集群节点、在哪个文件目录。那么最终ReplacingMergeTree表引擎在合并时,只会在当前节点、且物理位置在同一表目录下的数据进行merge操作。

最后,我们在设计表时,如果期望利用到ReplacingMergeTree自动去重的特性,那么必须使其存储在相同分区、相同分片下;而在设置分区键、分片键时,二者不要求必须相同,但必须稳定,稳定的含义是入参相同出参必须相同。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/102534.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(1)Linux搭建 zookeeper+kafka集群

因为之前公司业务都是使用mqtt接收数据,随着设备的增加,公司觉得用kafka集群来实现会更好 下面是我写一个demo 仅供参考 一、安装jdk 没有安装的可以百度去看看怎么安装的 不行的话在私聊我!!!! 二、搭建…

【Python】用turtle绘制“混沌皮”

画的不好看,不喜勿喷。 目录 展示​ 设置界面 绘制全身 绘制眼睛 全部代码 展示 我用的是turtle绘制的,不会的可以看这篇文章:【Python】turtle库的介绍及使用(计算机二级常考)_刘佳皓_Leo的博客-CSDN博客_t…

肝了一周总结的SpringBoot常用注解大全,一目了然!

平时使用SpringBoot开发项目,少不了要使用到它的注解。这些注解让我们摆脱了繁琐的传统Spring XML配置,让我们开发项目更加高效,今天我们就来聊聊SpringBoot中常用的注解! SpringBoot实战电商项目mall(50kstar&#xf…

aardio工程实例——MIDI音乐盒(源码)

前段时间,aardio增强了midiOut库相关功能,我结合这个库写了个程序,一方面自娱自乐,同时也给新接触aardio的朋友做个参考。 主要界面: 奉送两个乐谱: 外婆的澎湖湾 ________, 晚风轻拂澎湖湾 3,__,5,__,5,_…

车载以太网DoIP测试专栏 - 总纲

本专栏的目的:无论你是刚入行的小白还是对DoIP有一定工作经验的从业人员,保证在你完成这块的讲解后,首先让你了解DoIP要测试哪些?再者为何要测试这些是否还有更多的内容需要去测试,最后如何实现DoIP协议的测试&#xf…

智能制造工业互联简述

智能制造系统架构通过生命周期、系统层级和智能功能三个维度构建完成,主要解决智能制造标准体系结构和框架的建模研究 生命周期是由设计、生产、物流、销售、服务等一系列相互联系的价值创造活动组成的链式集合。生命周期中各项活动相互关联、相互影响。不同行业的生…

构造函数、原型和实例的关系

构造函数、原型和实例的关系: 每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型。如果原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另一个原型,相应地另…

[附源码]计算机毕业设计Python第三方游戏零售平台(程序+源码+LW文档)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等…

Unity实战篇 |Unity 打包exe 实现隐藏窗口标题栏、隐藏最小化最大化关闭按钮

🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN🙉 🎄 学习专栏推荐:Unity系统学习专栏 🌲 游戏制作专栏推荐:游戏制作 &…

【疾病分类】模糊逻辑分类叶病严重程度分级系统【含GUI Matlab源码 194期】

⛄一、模糊逻辑(Fuzzy Logic)简介 理论知识参考:模糊逻辑(Fuzzy Logic) ⛄二、部分源代码 function varargout LeafDiseaseGradingSystemGUI(varargin) % LeafDiseaseGradingSystemGUI MATLAB code for LeafDiseaseGradingSystemGUI.fig % LeafDiseaseGradingSy…

云原生 | go-micro@v4.9.0源码解析(建议收藏)

go-microv4.9.0源码阅读一、前言二、创建微服务三、源码阅读操作一:注册服务处理操作二:组件配置操作三:启动微服务Step 1 :启动微服务Step 2 :开启服务关闭监听Step 3 :停⽌Server组件Step 4 :…

毕业设计 基于java web的网上零食销售系统的设计与实现

文章目录前言一、项目设计1. 模块设计功能分析前台功能后台功能2. 实现效果二、部分源码项目源码前言 今天学长向大家分享一个 web项目: 基于java web的网上零食销售系统的设计与实现 一、项目设计 1. 模块设计 功能分析 经过对时下大型电商网站的调查,对该系统…

计算机毕业设计 SSM与Vue的垃圾分类系统(源码+论文)

文章目录1 项目简介2 实现效果2.1 界面展示3 设计方案3.1 概述3.2 系统流程3.2.1 系统开发流程3.2.2 登录流程3.3 系统结构设计4 最后1 项目简介 Hi,各位同学好呀,这里是M学姐! 今天向大家分享一个今年(2022)最新完成的毕业设计项目作品&am…

学人工智能好找工作吗 ?Python就业前景怎么样?

学人工智能好找工作吗?Python就业前景怎么样?人工智能前景很好,中国正在产业升级,工业机器人和人工智能方面都会是热点,而且正好是学习的好时机。但是,也有一个问题大家要注意: 学习的难度比较高…

[附源码]Python计算机毕业设计Django智能衣橱APP

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

数据类型相关笔记

C、C、java强调类型&#xff1b; JavaScript、python、php不看重类型&#xff0c;甚至不需要事先定义。 类型 ○ 整数 ○ 浮点数 ○ 逻辑 ○ 指针 ○ 自定义类型 sizeof 查看所占字节 给出某个类型或变量在内存中所占据的字节数 # include <stdio.h>int main(void) …

怎么把avi转成mp4格式?

怎么把avi转成mp4格式&#xff1f;这是最近大家问的比较频繁的一个问题&#xff0c;avi是一种音频视频的交错格式&#xff0c;可以极大的压缩视频文件本身所占用的内存空间&#xff0c;但是同时引起的负面缺点是使视频画质受损&#xff0c;所以大家在遇到avi格式视频时。可以转…

编程明明是程序员的工作,关学生什么事?

写在前面&#xff1a; 我手里这个魔方&#xff0c;任何一个人都可以轻松地把它打乱&#xff0c;但要想把它复原&#xff0c;就不是每个人都能做到的了。我会还原魔方&#xff0c;而你不会&#xff0c;这能不能说明我比你聪明呢&#xff1f;并不能。 还原一个三阶魔方是有特定的…

12基于储能电站服务的冷热电多微网系统双层优化配置(matlab程序)

参考文献 基于储能电站服务的冷热电多微网系统双层优化配置——吴盛军&#xff08;2020电网技术&#xff09; 主要内容 分析共享储能电站的运行方式和盈利机制。将储能电站服务应用到冷热电联供型多微网系统中&#xff0c;建立考虑两个不同时间尺度问题的双层规划模型&#…

pyTorch入门(四)——导出Minist模型,C++ OpenCV DNN进行识别

学更好的别人&#xff0c;做更好的自己。——《微卡智享》本文长度为2548字&#xff0c;预计阅读8分钟前言前三章介绍了pyTorch训练的相关&#xff0c;我们也保存模型成功了&#xff0c;今天这篇就是使用C OpenCV的DNN模块进行手写图片的推理。实现效果导出的推理模型使用的是M…