ClickHouse的Join算法

news2025/1/18 14:37:25

ClickHouse的Join算法

ClickHouse是一款开源的列式分析型数据库(OLAP),专为需要超低延迟分析查询大量数据的场景而生。为了实现分析应用可能达到的最佳性能,分析型数据库(OLAP)通常将表组合在一起形成一个大宽表,这个过程称为数据反规范化(data denormalization)。大宽表通过避免JOIN连接来帮助提升查询效率,代价是增加了ETL(从业务系统OLTP数据库里导出数据到ClickHouse)复杂性。

然而,在某些情况下并不能总是用大宽表来替代JOIN连接,有时分析查询的源数据的一部分需要保持规范化。这些规范化表占用较少的存储并提供数据组合的灵活性,但它们需要在查询时进行某些类型的JOIN连接操作。

虽然是分析型数据库并且倾向于大宽表的方式,但是ClickHouse是完全支持连接运算的。除了支持所有标准的SQL连接类型外,ClickHouse还提供更多的特殊连接类型。这些特殊的连接类型对分析工作负载和时间序列分析非常有用。ClickHouse提供6种不同的JOIN连接算法,并且可以选择让查询计划器根据具体情况选择或者在运行时动态更改JOIN连接算法。

即使在ClickHouse中对超大的数据表做JOIN连接运算,我们也可以通过精心选择连接算法和调优相关设置,从而得到非常良好的性能。虽然可以让ClickHouse更加聪明地帮用户做选择,但是目前效果毕竟有限,而且真正高级的性能调优是离不开人的,因为人能掌握更全面的情况,以及实际业务特点和需求。本文可以帮助你理解ClickHouse内部连接的工作方式,从而帮助你做相关的优化。

ClickHouse目前(2023年3月的版本)有6种JOIN连接算法:

  1. Hash Join
  2. Parallel Hash Join
  3. Grace Hash Join
  4. Full Sorting Merge Join
  5. Partial Merge Join
  6. Direct Join

以及CH支持的连接操作的种类有以下几种。除了Hash Join之外,其他JOIN连接算法只支持部分种类,因此如果需要某种JOIN连接运算时,只能在支持它的JOIN算法中选择。

  1. INNER JOIN
  2. OUTER JOIN
  3. CROSS JOIN
  4. SEMI JOIN
  5. ANTI JOIN
  6. ANY JOIN
  7. ASOF JOIN

不同的JOIN算法的特点

不同的JOIN的算法有不一样的时间和空间的执行代价,基本上是要么”时间换空间“,要么”空间换时间“,不会满足“既要-又要-还要”的。并且在不同特性的数据集(例如是否已按照JOIN关键字排过序)上各个JOIN算法的表现(时间和空间执行代价)也不一样。因此根据自身数据的特点、业务需求的特点以及计算和存储资源等限制,选择合适的JOIN算法变得非常有意义。

algorithms.png

根据上图,我们可以知道:(以上图表并不一定精确,受实际环境影响,但是上图勾勒出了大概的样子)

  • 最快的JOIN算法是DIRECT,但是要求条件是右表是已经构建好的Hash Map,或者严谨点来说是”可以快速读取的Key-Value容器“,目前支持这样的表引擎只有Dictionary和Join(对,有个表引擎叫Join Table Engine);
  • 除了DIRECT可能最快的JOIN算法是Parallel hash(实际上也不绝对,具体得看硬件配置),但也是最耗费内存的;
  • 最省内存的JOIN算法是Partial merge,但同时也是最慢的;
  • Grace hash的表现受buckets数量的配置影响,buckets数量增大后在节省内存方面媲美Partial merge,但是也牺牲了时间;
  • Full sorting merge的表现受JOIN运算的表是否按照JOIN关键字排序的影响(或者近似排序,后面会解释,只要相同JOIN关键字的行能聚在一起就行了)。在表是预先排序的情况下可以做到”既要-又要“的,这点是值得关注的。

Hash Join

Hash join算法是通过哈希表查表的连接运算算法。

hash.png

工作原理:

  1. 右侧表中的所有数据都被读取(通过 2 个线程并行,因为 max_threads = 2)并填充到内存中的哈希表中,哈希表的Key为Join Key,Value为需要用到的右侧表的列;
  2. 来自左侧表的数据被分数据块流式读取(由 2 个线程并行传输,因为 max_threads = 2,以下不再赘述);
  3. 通过查找哈希表来连接。

特点:

  • 支持所有的连接类型,对表引擎不限制;
  • 速度较快;
  • 内存消耗与右侧表的数据大小成正比;
  • 哈希表的构建和填充的最后一个合并步骤是单线程的,是可能的瓶颈。

Parallel Hash

Parallel hash join算法是在Hash join算法基础上引入了“分桶-多哈希表”的方式增加并行度而形成的连接算法。

parallel_hash.png

工作原理:

  1. 右侧表中的所有数据都被读取(通过 2 个线程并行,因为 max_threads = 2)并填充到内存中的“分桶的”多个哈希表(按照“分桶”策略,根据桶哈希函数选择某个哈希表),每个哈希表的Key为Join Key,Value为需要用到的右侧表的列;
  2. 来自左侧表的数据被分数据块流式读取;
  3. 根据相同“桶哈希函数”为左侧表的每行确定相应的哈希表,通过查找哈希表来连接。

跟Hash join的主要区别是“分桶”,这样解决了Hash join的“哈希表的构建和填充的最后一个合并步骤是单线程的”的瓶颈问题,Parallel hash join是多线程填充每个分桶的哈希表。

特点:

  • 支持INNER and LEFT JOIN,不支持ASOF,对表引擎不限制;
  • 速度很快;
  • 内存消耗与右侧表的数据大小成正比,比Hash join更消耗内存;
  • 分桶数决定性能,通常分桶数越多(但不要超过CPU核数)性能越好,但内存消耗更多;
  • 内存可能是瓶颈。

Grace Hash

Grace hash join算法是在Hash join算法中加上“分而治之”的策略。

grace_hash_1.png

工作原理:

  1. 右侧表中的所有数据都被读取(通过 2 个线程并行,因为 max_threads = 2)并按照分区哈希函数进行分桶,把属于第一个桶的数据填充到内存中的哈希表中,其他桶的数据临时存放在外存中,按桶区分(注意不是内存);
  2. 来自左侧表的数据被分数据块流式读取,同样分桶;
  3. 对于属于第一个桶的数据,通过查找当前内存中的第一个桶的哈希表(内存中也只有第一个桶的哈希表)来进行连接运算,完成后销毁哈希表;
  4. 对于其他桶的数据,按桶区分也临时存放在外存中;
  5. 第3和4步完成后,得到第一个桶的数据的连接结果已经分桶临时存放的左右两侧表的数据块;
  6. 对于桶编号 i(2 <= i <= n,n为桶最大编号),把右侧表桶i的数据填充到哈希表中;
  7. 读取左表桶i的数据,通过查找当前内存中哈希表来进行连接运算,完成后销毁哈希表;
  8. 依次完成桶2到桶n。

特点:

  • 支持INNER and LEFT JOIN,不支持ASOF,对表引擎不限制;
  • 涉及到分桶且存取外存,速度慢;
  • 连接用的哈希表总是局部数据,节约内存;
  • 内存消耗与左右侧表的数据大小没关系,理论上可以支持无限大小的左右表(只要外存够用);
  • 桶数越多,哈希表的数据越少,内存越省,但外存存取次数更多,速度更慢;
  • 外存存取是瓶颈。

Full Sorting Merge Join

首先需要知道Full sorting merge join与Partial merge join的思路与Hash类JOIN算法是不同的,属于另一类JOIN算法。

full_sorting_merge_abstract.png

Full sorting merge join算法利用了两个有序表之间可以直接匹配的特性(想象一下拉链),不需要构建哈希表。算法描述如下:

假设左侧表和右侧表均按照Join Key升序排序,设定两个游标L和R,L指向左侧表的第一行,R指向右侧表第一行。L指向行的Join Key与R指向的行的Join Key的比较关系只会有三种:

  • 等于

    匹配上了,得到两行连接结果。

  • 大于

    右表游标R向下移动一行,因为右表能匹配上的行只会在后面。

  • 小于

    左表游标L向下移动一行,因为左表能匹配上的行只会在后面。

full_sorting_merge_1.png

工作原理:

  1. 左侧表和右侧表分别合并排序,并在可能的情况下借助外存(参见借助外存的合并排序);
  2. 按照上述的算法进行连接运算。

可能的优化办法有两个:

  1. 减少参与排序和合并理解的数据量;
  2. 事先将表按照Join Key排序(或者反过来说,只在这个情况下选择此算法)。

第一个优化办法是假设左右两侧表中的很多数据并不在连接结果里(即有很多根本不会匹配上的行)。这个方法会试图建立左右两侧表的Join Key集合,通过过滤掉左右侧表的“不可能连接”的数据行来减少排序和合并连接的数据量。Join Key集合不可能无限大,由max_rows_in_set_to_optimize_join设置控制,超过限制则取消优化。

第二个优化办法就是直接跳过最耗时的排序步骤,直接合并连接。

特点:

  • 支持INNER, LEFT, RIGHT, and FULL连接类型,对表引擎不限制;
  • 在左右两侧表事先按照Join Key排序(或者Join Key是排序键列表的前部分)存储的情况下速度较快且内存省;
  • 内存消耗与左右侧表的数据大小没关系,理论上可以支持无限大小的左右表(只要外存够用);
  • 左右侧表排序是瓶颈,特别是数据量大需要借用外存进行排序的情况。

关于Join Key是排序键列表的前部分的解释:

假设表T的排序键是A、B、C,Join Key A、B就是属于这种情况。

Partial Merge Join

Partial merge join算法与Full sorting merge join算法类似,不同的是它不会全局排序左表,只会排序右表并按数据块存储在外存中,并建立min-max索引(该索引记录了每个数据块的最小Join Key和最大Join Key)。扫描左表执行JOIN连接运算。

partial_merge_abstract.png

工作原理:

  1. 用外部排序算法排序右侧表,存入外存并建立min-max索引;
  2. 分数据块流式读取左侧表的数据,对数据块局部排序,根据min-max索引定位到右侧表可能的数据块范围;
  3. 按照merge-join算法(与full sorting merge join一样的)完成左侧表该数据块的连接JOIN运算;
  4. 重复2,3步,完成左侧表所有的数据的JOIN运算。

partial_merge_1.png

缓存在外存中的排好序的右侧表的数据块不应该全部参与某个左侧表数据块的连接运算,而是应该尽量利用Min-max索引过滤掉哪些不需要参与连接运算的右侧表的数据块。如果左表的物理行顺序与连接键排序顺序匹配,则左表中某个数据块的Join Key的最大值和最小值变得很窄,可以过滤掉大部分的右侧表的数据块。但是如果左侧表的数据在Join Key上分布很平均,意味着每个左侧表的数据块的Join Key的最大值和最小值都差不多,这种情况下是过滤不了多少右侧表的数据块的,效率就变得非常低。

特点:

  • 支持INNER, LEFT, RIGHT, and FULL连接类型,对表引擎不限制;
  • 在左右两侧表事先按照Join Key排序(或者Join Key是排序键列表的前部分)存储的情况下速度较快且内存省;
  • 内存消耗与左右侧表的数据大小没关系,理论上可以支持无限大小的左右表(只要外存够用);
  • 通常内存效率非常高,但是执行速度相应地比Full sorting merge join要低;
  • 当左侧表是排序的时候,右侧表的min-max索引能够发挥最大作用,反之左侧表越无序,右侧表的min-max索引的作用就越小;
  • 右侧表排序是瓶颈,左侧表在Join Key上无序情况下是Merge join的操作是瓶颈。

Direct Join

Direct join连接算法是高速版的Hash join连接算法,高速的原因是省去了构建哈希表的这个费时过程。

direct_1.png

工作原理:

  1. 右侧表是Key-value结构的表引擎,其数据驻留内存可以直接当哈希表使用;
  2. 来自左侧表的数据被分数据块流式读取,通过查找右侧表来完成连接运算。

特点:

  • 仅支持LEFT ANY连接,右表引擎必须为Dictionary或者Join,且Join Key必须是右侧表的Key;
  • 速度很快;
  • 无额外内存消耗(但右侧表数据本来就在内存中);
  • 几乎无瓶颈。

JOIN算法的选择

首先确定哪些JOIN算法支持所需要执行的连接的场景,然后根据性能图谱按照速度优先或者内存优先来选择合适的JOIN算法。

过滤不支持的JOIN算法

根据应用场景过滤掉不支持的连接类型。如下表所示,Hash join支持的最全,也是最早投入使用的Join算法之一。Direct join支持的最少且对右侧表的表引擎有额外限制。另外一个支持比较多的且性能比较好的是Full sorting merge join。

choosing_join_3.png

性能图谱

下图展示了所有JOIN算法的时间和空间性能的特点。Direct是最快的,但通用性不强;Parallel在速度上仅次于Direct join,但是内存使用是最高的;Full sorting merge join根据表的排序情况在性能上有很大差别;Grace hash join的性能受buckets数量影响很大;最省内存也是通常最慢的,这就是Partial merge join。

algorithms.png

性能评测参考结果

下面是一些用IMDB数据为测试数据的各种JOIN连接算法的性能评测。

1百万 连接 1亿

imdb_large.png

1亿 连接 10亿

imdb_xlarge.png

根据以上性能图谱和实际评测结果,形成以下JOIN算法选择策略。

以性能为优先选择

按照从上到下的优先级顺序以此判断:

  1. 以下条件满足时,选择Direct join
    • 右侧表的表引擎为Dictionary或者Join的时候,且Join Key是或者可以转化为是右侧表的Key属性(Dictionary和Join表引擎都会定义Key属性)
    • 连接类型为LEFT ANY JOIN
  2. 以下条件满足时,选择Full sorting merge join(需要把max_rows_in_set_to_optimize_join设置为0以启用相应优化)
    • 左侧表的物理顺序匹配连接运算的Join Key
    • 右侧表的物理顺序匹配连接运算的Join Key
  3. 以下条件满足时,选择Hash join或者Parallel hash join
    • 右侧表数据可以全部填充进内存中的哈希表中
    • 如果内存足够且希望充分利用多核,选择Parallel hash join
  4. 其他情况下,选择Grace hash join、Full sorting merge join或者Partial merge join,并可以做以下微调:
    • Grace hash可以根据调整bucket数量在 (速度慢,内存少)到(速度快,内存多)之间找一个平衡点
    • 如果左表的Join Key分布平均,则避免使用Partial merge join
    • 调整max_rows_in_set_to_optimize_join设置以达到Full sorting merge join的in-set 过滤优化的最佳效果

以内存为优先选择

按照从上到下的优先级顺序以此判断:

  1. 以下条件满足时,选择Full sorting merge join
    • 左侧表的物理顺序匹配连接运算的Join Key
    • 右侧表的物理顺序匹配连接运算的Join Key
  2. 内存哈希表无法装下右侧表数据的情况下,选择 Grace hash join或者Partial merge join
  3. Hash join
  4. Parallel hash join

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

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

相关文章

MOS管为什么会存在寄生电感

说到MOS管的寄生参数&#xff0c;我们一般都只想到mos管各极间的寄生电容&#xff0c;很少会想到MOS管的寄生电感。 其实分立的MOS管它是存在寄生电感的&#xff0c;并且栅极&#xff0c;源极和漏极都存在。 在一些MOS的数据手册会提到这个寄生电感。 那么MOS管寄生电感是怎么产…

9月4日上课内容 第七章 案例:MHA高可用配置及故障切换

本章结构 案例概述 案例前置知识点 1&#xff0e;什么是 MHA&#xff08;MHA概念&#xff09; MHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出现就是解决MySQL 单点的问题。 MySQL故障切换过程中&…

ComPtr源码分析

ComPtr源码分析 ComPtr是微软提供的用来管理COM组件的智能指针。DirectX的API是由一系列的COM组件来管理的&#xff0c;形如ID3D12Device&#xff0c;IDXGISwapChain等的接口类最终都继承自IUnknown接口类&#xff0c;这个接口类包含AddRef和Release两个方法&#xff0c;分别用…

BUUCTF easyre 1

使用die工具进行文件信息的查看 可以看到是64位程序 使用IDA64打开 f5 反汇编 得到flag

超详细Python第三方库的安装,多图,逐步骤

Python第三方库的安装 前言1. PyCharm中安装模块2. PyCharm终端中命令安装3. 命令行安装4. 命令补充 总结 前言 Python有丰富的第三方库&#xff0c;在Python编程中&#xff0c;经常需要安装第三方库&#xff0c;本文详细介绍了第三方模块/软件包的安装。 提示&#xff1a;大家…

手写Spring:第17章-通过三级缓存解决循环依赖

文章目录 一、目标&#xff1a;通过三级缓存解决循环依赖二、设计&#xff1a;通过三级缓存解决循环依赖2.1 通过三级缓存解决循环依赖2.2 尝试使用一级缓存解决循环依赖 三、实现&#xff1a;通过三级缓存解决循环依赖3.1 工程结构3.2 通过三级缓存解决循环依赖类图3.3 设置三…

Kafka3.0.0版本——消费者(手动提交offset)

目录 一、消费者&#xff08;手动提交 offset&#xff09;的概述1.1、手动提交offset的两种方式1.2、手动提交offset两种方式的区别1.3、手动提交offset的图解 二、消费者&#xff08;手动提交 offset&#xff09;的代码示例2.1、手动提交 offset&#xff08;采用同步提交的方式…

JavaScript-----jQuery

目录 前言&#xff1a; 1. jQuery介绍 2. 工厂函数 - $() jQuery通过选择器获取元素&#xff0c;$("选择器") 过滤选择器&#xff0c;需要结合其他选择器使用。 3.操作元素内容 4. 操作标签属性 5. 操作标签样式 6. 元素的创建,添加,删除 7.数据与对象遍历…

torch.cuda.is_available() 解决方

本人使用的显卡如下&#xff0c;打开任务管理器查看 Anaconda下载哪个版本都可以 使用命令conda create -n pytorch python3.6创建一个名为pytorch的环境&#xff0c;解释器使用3.6的 使用命令conda activate pytorch进入该环境 进入pytorch官网&#xff0c;选择下列选项 复…

九 动手学深度学习v2 ——卷积神经网络之AlexNet

文章目录 AlexNetVGG AlexNet AlexNet新引入dropout、ReLU、maxpooling和数据增强。 VGG VGG神经网络连接 图7.2.1的几个VGG块&#xff08;在vgg_block函数中定义&#xff09;。其中有超参数变量conv_arch。该变量指定了每个VGG块里卷积层个数和输出通道数。全连接模块则与Ale…

超详细最新PyCharm+Python环境安装,多图,逐步骤

PyCharmPython环境安装 前言一、pycharm下载安装1. 安装地址2. 安装详细步骤 二、Python下载安装1. 安装地址2. 安装详细步骤3. 环境变量忘记添加4. python安装成功测试 三. PyCharm上配置Python总结推荐文章 前言 文章会详细介绍PyCharmPython详细安装步骤&#xff0c;接下来…

【数据分享】2012-2023年全国范围的逐月NPP/VIIRS夜间灯光数据

在之前的文章中我们分享了2012-2022年全球范围逐年NPP/VIIRS夜间灯光数据&#xff01;以及2012-2022年中国范围的逐年NPP/VIIRS夜间灯光数据&#xff08;均可查看之前的文章获悉详情&#xff09;很多小伙伴询问有没有逐月的夜间灯光数据。 本次我们分享的是2012-2023年中国范围…

Flask 快速上手教程 — 了解与基本使用

Flask 快速上手教程 — 了解与基本使用 这篇博客是我刚接触 flask&#xff0c;研究文档时的一些记录与体会&#xff0c;希望对各位刚接触 flask 的朋友有所帮助。 且在此篇后&#xff0c;我还会另写一篇关于纯后端的 flask 教程&#xff0c;介绍一下如何使用 flask 创建一个较…

Oracle for Windows安装和配置——2.1.Oracle for Windows安装

​2.1.1. 准备Oracle软件 1)下载或拷贝安装软件 下载地址:otn.oracle.com或my oracle support。下载文件列表。具体如图2.1.1-1所示。图2.1.1-1 下载文件列表 --说明: 1)通过otn.oracle.com站点,可以免费下载用于安装的Oracle软件,但通常只能下载到Oracle各大版本的base…

切面(增强)的优先级

Component Aspect Order(value 10)//为增强类指定一个优先级的值,值越小,优先级越高,优先级越高的前置先执行,后置后执行,类似洋葱 为增强类指定一个优先级的值,值越小,优先级越高,优先级越高的前置先执行,后置后执行,类似洋葱 首先会执行前置通知,再执行目标方法,按照顺序和优…

并查集介绍和常用模板

并查集介绍和常用模板 前言&#xff1a; 并查集&#xff08;Union-find set 也叫Disjoint Sets&#xff09;是图论里面一种用来判断节点之间是否连通的数据结构&#xff0c;学会使用它可以处理一些跟节点连通性的问题。它有两个很重要的方法&#xff1a; Find(x)&#xff1a;…

2023软件设计师上半年真题解析(上午+下午)

上午试题 1.系统总线 计算机中&#xff0c;系统总线用于&#xff08;&#xff09;连接。 A&#xff0e;接口和外设 B&#xff0e;运算器,控制器和寄存器C&#xff0e;主存及外设部件 D&#xff0e;DMA控制器和中断控制器 总线可以划分为数据总线、地址总线和控制总线。系统总…

自然语言处理应用(一):情感分析

情感分析 随着在线社交媒体和评论平台的快速发展&#xff0c;大量评论的数据被记录下来。这些数据具有支持决策过程的巨大潜力。 情感分析&#xff08;sentiment analysis&#xff09;研究人们在文本中 &#xff08;如产品评论、博客评论和论坛讨论等&#xff09;“隐藏”的情…

面向OLAP的列式存储DBMS-16-[ClickHouse]python操作ClickHouse

clickhouse查询表容量方法 1 clickhouse常用命令 #clickhouse-client进入客户端 pda1:)show databases; pda1:)create database test; pda1:)use system; pda1:)show tables; pda1:) exit; 其余的就是常规的一些sql语句。 2 python操作clickhouse 2.1 clickhouse-driver(9…

Windows下python,psycopg2使用sm3连接HGDB

瀚高数据库 目录 环境 文档用途 详细信息 环境 系统平台&#xff1a;N/A 版本&#xff1a;4.5 文档用途 本文介绍在HGDB使用sm3认证时&#xff0c;python使用psycopg2连接HGDB的方法。 详细信息 Python连接HGDB可以使用psycopg2、Django&#xff0c;Django是依赖psycopg2的&a…