如何让 JOIN 跑得更快?

JOIN 一直是数据库性能优化的老大难问题,本来挺快的查询,一旦涉及了几个 JOIN,性能就会陡降。而且,参与 JOIN 的表越大越多,性能就越难提上来。

其实,让 JOIN 跑得快的关键是要对 JOIN 分类,分类之后,就能利用各种类型 JOIN 的特征来做性能优化了。

JOIN 分类

有 SQL 开发经验的同学都知道,绝大多数 JOIN 都是等值 JOIN,也就是关联条件为等式的 JOIN。非等值 JOIN 要少见得多,而且多数情况也可以转换成等值 JOIN 来处理,所以我们可以只讨论等值 JOIN。

等值 JOIN 主要又可以分为两大类:外键关联和主键关联。

外键关联是指用一个表的非主键字段,去关联另一个表的主键,前者称为事实表,后者为维表。比如下图中,订单表是事实表,客户表、产品表、雇员表是维表。

在这里插入图片描述

外键表是多对一关系,而且是不对称的,事实表和维表的位置不能互换。需要说明的是,这里说的主键是指逻辑上的主键,也就是在表中取值唯一、可以用于唯一确定某条记录的字段(或字段组),不一定在数据库表上建立过主键。

主键关联是指用一个表的主键关联另一个表的主键或部分主键。比如下图中客户和 VIP 客户、订单表和订单明细表的关联。

在这里插入图片描述

客户和 VIP 客户按照主键关联,这两个表互为同维表。订单则是用主键去关联明细的部分主键,我们称订单表是主表,明细表是子表。

同维表是一对一关系。且同维表之间是对称的,两个表的地位相同。主子表则是一对多关系,而且是不对称的,有明确的方向。仔细观察会发现,这两类 JOIN 都涉及到主键了。

而不涉及主键的 JOIN 会导致多对多关系,大多数情况都没有业务意义。换句话说,上述这两大类 JOIN 涵盖了几乎全部有业务意义的 JOIN。

如果我们能利用 JOIN 总会涉及主键这个特征做性能优化,能解决掉这两大类 JOIN,其实也就意味着解决了大部分 JOIN 性能问题。

但是,SQL 对 JOIN 的定义并不涉及主键,只是两个表做笛卡尔积后再按某种条件过滤。这个定义很简单也很宽泛,几乎可以描述一切。

但是,如果严格按这个定义去实现 JOIN,也就没办法在性能优化时利用主键的特征了。SPL 改变了 JOIN 的定义,专门针对这两大类 JOIN 分别处理,利用了主键的特征减少运算量,从而实现性能优化的目标。

下面我们来看看 SPL 具体是怎么做的。

外键关联

如果事实表和维表都不太大,可以全部装入内存,SPL 提供了外键地址化方法:先把事实表中的外键字段值转换为对应维表记录的地址,之后引用维表字段时,就可以用地址直接取出了。

以前面的订单表、雇员表为例,假定这两个表已经被读入内存。外键地址化的工作机制是这样的:对于订单表某记录 r 的 eid 字段,到雇员表中找到这个 eid 字段值对应的记录,得到其内存地址 a,再将 r 的 eid 字段值替换成 a。对订单表的所有记录都做好这样的转换,就完成了外键地址化。

这时候,订单表记录 r 要引用雇员表字段时,直接用 eid 字段存储的地址 a 取出雇员表记录和字段就可以了,相当于常数时间内就能取得雇员表的字段,不需要再到雇员表做查找。可以在系统启动时把事实表和维表读入内存,并一次性做好外键地址化,即预关联。

这样,在后续关联计算时就能直接用事实表外键字段中的地址去取维表记录,完成高性能的 JOIN 计算。外键地址化和预关联的详细原理请参考:http://c.raqsoft.com.cn/article/1616970721547

SQL 通常使用 HASH 算法来做内存连接,需要计算 HASH 值和比对,性能会比直接用地址读取差很多。

SPL 之所以能实现外键地址化,是利用了维表的关联字段是主键这一特征。上面例子中,关联字段 eid 是雇员表的主键,具有唯一性。

订单表中的每个 eid 只会唯一对应一条雇员记录,所以才能把每个 eid 转换成它唯一对应的那条雇员记录的地址。而 SQL 对 JOIN 的定义中没有主键的约定,就不能认定与事实表中外键关联的维表记录有唯一性,有可能发生与多条记录关联的情况。

对于订单表的记录来讲,eid 值没有办法唯一对应一条雇员记录,就无法做到外键地址化了。而且 SQL 也没有记录地址这种数据类型,结果会导致每次关联时还是要计算 HASH 值并比对。

只是两个表 JOIN 时,外键地址化和 HASH 关联的差别还不是非常明显。这是因为 JOIN 并不是最终目的,JOIN 之后还会有其它很多运算,JOIN 本身运算消耗时间的占比相对不大。但事实表常常会有多个维表,甚至维表还会有很多层。比如订单关联产品,产品关联供应商,供应商关联城市,城市关联国家等等。在关联表很多时,外键地址化的性能优势会更明显。

下面的测试,在关联表个数不同的情况下对比 SPL 与 Oracle 的性能差异,可以看出在表很多时,外键地址化的优势相当明显:

测试的详细情况请参考:性能优化技巧:预关联。

对于只有维表能装入内存,而事实表很大需要外存的情况,SPL 提供了外键序号化方法:预先将事实表中的外键字段值转换为维表对应记录的序号。

关联计算时,分批读入新事实表记录,再用序号取出对应维表记录。

以上述订单表、产品表为例,假定产品表已经装入内存,订单表存储在外存中。外键序号化的过程是这样:先读入一批订单数据,设其中某记录 r 中的 pid 对应的是内存中产品表的第 i 条记录。

我们要将 r 中的 pid 字段值转换为 i。对这批订单记录都完成这样的转换后,再做关联计算时,从外存中分批读入订单数据。

对于其中的记录 r,就可以直接根据 pid 值,去内存中的产品表里用位置取出相应的记录,也避免了查找动作。

外键序号化原理更详细的介绍参考:http://c.raqsoft.com.cn/article/1617144101332

数据库通常会把小表读入内存,再分批读入大表数据,用哈希算法做内存连接,需要计算哈希值和比对。而 SPL 使用序号定位是直接读取,不需要进行任何比对,性能优势比较明显。虽然预先把事实表的外键字段转换成序号需要一定成本,但这个预计算只需要做一次,而且可以在多次外键关联中得到复用。SPL 外键序号化同样利用了维表关联字段是主键的特征。

如前所述,SQL 对 JOIN 的定义没有主键的约定,无法利用这一特征做到外键序号化。另外,SQL 使用无序集合的概念,即使我们事先把外键序号化了,数据库也无法利用这个特点,不能在无序集合上使用序号快速定位的机制,最快也就是用索引查找。

而且,数据库并不知道外键被序号化了,仍然会去计算 HASH 值和比对。下面这个测试,在不同并行数情况下,对比 SPL 和 Oracle 完成大事实表、小维表关联计算的速度,SPL 跑的比 Oracle 快 3 到 8 倍。

测试结果见下图:

这个测试更详细的信息请参考:性能优化技巧:外键序号化。

如果维表很大也需要外存,而事实表较小能装入内存,SPL 则提供了大维表查找机制。

如果维表和事实表都很大,SPL 则使用单边分堆算法。对于维表过滤后再关联的情况,SPL 提供了索引复用方法及对位序列等方法。

数据量大到需要分布式计算时,如果维表较小,SPL 采用复写维表机制,将维表在集群节点上复制多份;如果维表很大,则采用集群维表方法以保证随机访问。这两种方法都可以有效的避免 Shuffle 动作。相比而言,SQL 体系下不能区分出维表,HASH 拆分方法要将两个表都做 Shuffle 动作,网络传输量要大得多。

主键关联

主键关联涉及的表一般都比较大,需要存储在外存中。SPL 为此提供了有序归并方法:预先将外存表按照主键有序存储,关联时顺序取出数据做归并计算。

以客户和 VIP 客户两个表做内连接为例,假设已经预先将两个表按照主键 cid 有序存储在外存中。关联时,从两个表的游标中读取记录,逐条比较 cid 值。如果 cid 相等,则将两表的记录合并成结果游标的一条记录返回。如果不相等,则 cid 小的那个游标再读取记录,继续判断。重复这些动作直到任何一个表的数据被取完,返回的游标就是 JOIN 的结果。

对于两个大表关联,数据库通常使用哈希分堆算法,复杂度是乘法级的。而有序归并算法复杂度是加法级,性能会好很多。而且,数据库做大数据的外存运算时,哈希分堆会产生缓存文件的读写动作。有序归并算法则只需要对两个表依次遍历,不必借助外存缓存,可以大幅降低 IO 量,有巨大的性能优势。

预先按照主键排序的成本虽高,但是一次性做好即可,以后就总能使用归并算法实现 JOIN,性能可以提高很多。同时,SPL 也提供了在有追加数据时仍然保持数据整体有序的方案。

这类 JOIN 的特征在于关联字段是主键或部分主键,有序归并算法正是根据这个特征来设计的。因为不管是同维表还是主子表,关联字段都不会是主键之外的其他字段,所以我们将关联表按照主键有序这一种方式排序存储就可以了,不会出现冗余。而外键关联就不具备这个特征,不能使用有序归并。具体来说,是因为事实表的关联字段不是主键,会存在多个要参与关联的外键字段,我们不可能让同一个事实表同时按多个字段都有序。

SQL 对 JOIN 的定义不区分 JOIN 类型,不假定某些 JOIN 总是针对主键的,就没办法从算法层面上利用主键关联的特征。而且,前面说过 SQL 基于无序集合概念,数据库不会刻意保证数据的物理有序性,很难实施有序归并算法。

有序归并算法的优势还在于易于分段并行。以订单和订单明细按 oid 关联为例,假如将两表都按照记录数大致平均分为 4 段,订单第 2 段的 oid 有可能会出现在明细第 3 段,类似的错位会导致错误的计算结果。SPL 再次利用主键 oid 的有序性,提供同步分段机制,解决了这个问题:先将有序的订单表分为 4 段,再找到每一段起止记录的 oid 值形成 4 个区间,将明细表也分成同步的 4 段。这样,在并行计算时两表对应分段就不会出现错位了。由于明细表也对 oid 有序,可以迅速地按照起止 oid 定位,不会降低有序归并的性能。

有序归并和同步分段并行的原理,详见:SPL 有序归并关联 http://c.raqsoft.com.cn/article/1647665012651

传统的 HASH 分堆技术实现并行就比较困难了,多线程做 HASH 分堆时需要同时向某个分堆写出数据,造成共享资源冲突;而下一步实现某组分堆关联时又会消费大量内存,无法实施较大的并行数量。

实际测试证明,在相同情况下,我们对两个大表做主键关联测试(详情参见性能优化技巧:有序归并),结果是 SPL 比 Oracle 快了近 3 倍:

除了有序归并,SPL 还提供了很多高性能算法,全面提高主键关联 JOIN 的计算速度。包括:附表机制,可以将多表一体化存储,减少存储数据量的同时,还相当于预先完成了关联,不需要再比对了;关联定位算法,实现先过滤再关联,可以避免全表遍历,获得更好的性能等等。

当数据量继续增加,需要多台服务器集群时,SPL 提供复组表机制,将需要关联的大表按照主键分布到集群节点上。相同主键的数据在同一节点,避免分机之间的数据传输,也不会出现 Shuffle 动作。

回顾与总结

回顾上面两大类、各场景 JOIN,采用 SPL 分情况提供的高性能算法,可以利用不同类型 JOIN 的特征提速,让 JOIN 跑得更快。SQL 对上述这么多种 JOIN 场景笼统的处理,就没办法针对不同 JOIN 的特征来实施这些高性能算法。比如:事实表和维表都装入内存时,SQL 只能按照键值计算 HASH 和比对,无法利用地址直接对应;SQL 数据表无序,在大表按照主键关联时无法做到有序归并,只能使用 HASH 分堆,有可能会出现多次缓存的现象,性能有一定的不可控性。

并行计算方面,SQL 单表计算时还容易做到分段并行,多表关联运算时一般就只能事先做好固定分段,很难做到同步动态分段,这就难以根据机器的负载临时决定并行数量。

对于集群运算也是这样,SQL 在理论上不区分维表和事实表,要实现大表 JOIN 就会不可避免地产生占用大量网络资源的 HASH Shuffle 动作,在集群节点数太多时,网络传输造成的延迟会超过节点多带来的好处。

SPL 设计并应用了新的运算和存储模型,可以在原理和实现上解决 SQL 的这些问题。对于 JOIN 的不同分类和场景,程序员有针对性的采取上述高性能算法,就能获得更快的计算速度,让 JOIN 跑得更快。

SPL下载地址:集算器 (SPL) 最新版发布啦『发布日期 20220402』 - 乾学院

SPL开源地址:https://github.com/SPLWare/esProc

SPL近年引用最高论文

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++max函数的使用案例20个

文章目录 1. **基本用法:**2. **比较浮点数:**3. **比较字符串:**4. **使用自定义比较函数:**5. **比较容器中的元素:**6. **使用std::initializer_list:**7. **变长参数版本(C11及以上&#xf…

c++的队列的用法

基本介绍 c的队列就是std::queue。 需要包含的头文件&#xff1a; #include<queue>queue就是先进先出队列 queue,就是队列&#xff0c;队列是一种容器适配器&#xff0c;专门设计用于在FIFO上下文中操作(先进先出)&#xff0c;其中将元素插入容器的一端并从另一端提…

广东Lenovo SR588服务器维修升级硬盘内存

本案例描述了对联想SR588服务器进行硬件升级的过程&#xff0c;包括更换固态硬盘作为系统盘&#xff0c;以及增加内存容量至128GB。升级后&#xff0c;服务器性能得到显著提升&#xff0c;同时通过重新配置RAID阵列和操作系统的重新安装&#xff0c;确保了系统的稳定性和数据的…

华为路由器 三层交换实现vlan通信 实验(附ensp实验源文件)

一、目标 通过三层交换实现vlan通信 二、网络配置 2.1 交换机1&#xff08;LSW1&#xff09;配置 # 创建两个vlan vlan 10 vlan 20# 配置g0/0/1端口 port link-type access port default vlan 10# 配置g0/0/2端口 port link-type access port default vlan 20# 配置g0/0/3端口…

STM32F1 - SPI读写Flash

Serial peripheral interface 1> 实验概述2> SPI硬件框图初始化程序 3> STM32的SPI通信时序3.1> 时序图3.2> 文字描述3.3> 注意事项3.4> 流程图表示3.5> 程序表示接收程序&#xff1a;发送程序&#xff1a; 4> SPI的4种模式5> W25Q128存储结构块…

开发知识点-Python-爬虫

爬虫 scrapybeautifulsoupfind_all find祖先/父节点兄弟节点nextpreviousCSS选择器属性值 attrsselect 后 class 正则使用字符串来描述、匹配一系列符合某个规则的字符串组成元字符使用grep匹配正则组与捕获断言与标记条件匹配正则表达式的标志 特定中文 匹配 scrapy scrapy内…

ssm+springboot音乐播放器网站mybatis+jsp

测试流程 &#xff08;1&#xff09; 登录系统、填写用户名、密码选择角色&#xff0c;主要内容&#xff1a;进行权限控制。 &#xff08;2&#xff09; 用户查看音乐信息、音乐资讯功能&#xff0c;主要是测试系统实用性、方便性。 &#xff08;3&#xff09; 信息修…

B084-SpringCloud-Zuul Config

目录 zuul系统架构和zuul的作用zuul网关实现配置映射路径过滤器 Config概述云端管理本地配置 zuul zuul是分布式和集群后前端统一访问入口 系统架构和zuul的作用 zuul把自己注册进eureka&#xff0c;然后可通过前端传来的服务名发现和访问对应的服务集群 为了预防zuul单点故…

在Java中处理JSON数据:Jackson与Gson库比较

引言 JSON&#xff0c;作为一种轻量级的数据交换格式&#xff0c;因其易于人阅读和编写&#xff0c;同时也易于机器解析和生成&#xff0c;而被广泛应用于网络通信和配置文件中。在Java中&#xff0c;有两个强大的工具帮助咱们处理JSON数据——Jackson和Gson。这两个库各有千秋…

Conda 快速入门,轻松拿捏 Python

一、简介 Conda 是一个开源的软件包管理系统和环境管理系统&#xff0c;用于安装多个版本的软件包及其依赖关系&#xff0c;并在它们之间轻松切换。Conda 是为 Python 程序创建的&#xff0c;适用于 Linux&#xff0c;OS X 和Windows&#xff0c;也可以打包和分发其他软件&…

teknoparrot命令行启动游戏

官方github cd 到teknoparrot解压目录 cd /d E:\mn\TeknoParrot2_cp1\GameProfiles启动游戏 TeknoParrotUi.exe --profile游戏配置文件游戏配置文件位置/UserProfiles,如果UserProfiles文件夹里没有那就在/GameProfiles,在配置文件里将游戏路径加入之间,或者打开模拟器设置 …

SpringCloud Alibaba 学习

一&#xff1a;SpringCloud Alibaba介绍 Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服 务的必需组件&#xff0c;方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。 依托 Spring Cloud Alibaba&…

mybatis不重复列插入例子详细说明

之前有做过不重复列插入的需求&#xff0c;当时是 在插入时判断 对应的列在数据库中有没有对应的数据 有则返回false 无则插入&#xff0c;但是这加大了数据库的查询负担 也增加了插入的时间&#xff0c;故今天研究一下 使用sql来简化了这一点 使用的知识点是 daul表 insert i…

提升效率的电脑定时工具,AutoOff软件推荐

今天最软库给大家带来一款非常实用的电脑定时关机软件在我们日常办公的时候有的时候需要上传一些资料由于我们下班了&#xff0c;我们想让他上传完成之后我们才离开这时候呢&#xff0c;就可以用到这款定时工具了。 我们可以设置中设置在几小时或者几分钟之后让电脑进行关机我们…

JDK、JRE、JVM的联系区别

在第一章中我们介绍了JDK的下载配置与IDEA开发环境的下载安装&#xff0c;以及分别在这两个&#xff08;电脑本机和IDEA&#xff09;环境上执行了我们的第一个源程序。通过直观的使用&#xff0c;我们可以感受到集成开发环境的便捷。 大家也更加对JDK有了直观的了解&#xff0c…

【千字总结】爬虫学习指南-2024最新版

介绍 如何自学爬虫&#xff1f;今天有一个兄弟这样问我&#xff0c;可以看到打了很多字&#xff0c;诚意肯定是很足的&#xff0c;也是对我的内容给予了肯定&#xff0c;让我非常的开心。既然难得有人问我&#xff0c;那我一定要好好做一个回答。 我下面将要说的内容没有任何话…

制作耳机壳的UV树脂和塑料材质相比优势有哪些?

制作耳机壳的UV树脂相比塑料材质有以下优势&#xff1a; 高强度与耐磨性&#xff1a;UV树脂具有高强度和耐磨性&#xff0c;能够更好地保护耳机内部零件&#xff0c;延长耳机使用寿命。相比之下&#xff0c;塑料材质可能较易磨损或刮伤。耐高温&#xff1a;UV树脂具有较好的耐…

探索Java开发面试笔记:以听为目的,助力编程技术提升与面试准备

文章目录 一、制作背景介绍二、 Java开发面试笔记&#xff1a;为你的编程之路加速2.1 公众号主题和目标读者群体2.2 为什么面试笔记对于提高编程视野和技术至关重要2.3 親測效率 三、形式案例3.1 文章形式3.2 手机案例3.3 电脑案例 一、制作背景介绍 做公众号的背景&#xff1a…

瑞_23种设计模式_享元模式

文章目录 1 享元模式&#xff08;Flyweight Pattern&#xff09;1.1 介绍1.2 概述1.3 享元模式的结构1.4 享元模式的优缺点1.5 享元模式的使用场景 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代码实现 4 JDK源码解析&#xff08;Integer类&#xff09; &#x1f64a; …

公园常见污水处理需要哪些设备

根据我了解的情况&#xff0c;公园常见的污水处理需要以下几种设备&#xff1a; 1、格栅机&#xff1a;格栅机是污水处理的第一道工序&#xff0c;用于过滤掉污水中的大颗粒杂物和固体废物&#xff0c;防止其进入后续处理装置。 2、沉淀池&#xff1a;沉淀池用于将污水中的悬浮…