返回顶部
首页 > 资讯 > 数据库 >如何理解MySQL的join功能
  • 472
分享到

如何理解MySQL的join功能

2024-04-02 19:04:59 472人浏览 安东尼
摘要

这篇文章主要讲解了“如何理解Mysql的join功能”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解mysql的join功能”吧!正文在日常数据库查询

这篇文章主要讲解了“如何理解Mysql的join功能”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解mysql的join功能”吧!

正文

在日常数据库查询时,我们经常要对多表进行连表操作来一次性获得多个表合并后的数据,这是就要使用到数据库的 join 语法。join  是在数据领域中十分常见的将两个数据集进行合并的操作,如果大家了解的多的话,会发现 Mysqloraclepostgresqlspark  都支持该操作。本篇文章的主角是 MySQL,下文没有特别说明的话,就是以 MySQL 的 join 为主语。而 Oracle ,PostgreSQL 和  Spark 则可以算做将其吊打的大boss,其对 join 的算法优化和实现方式都要优于 MySQL。

MySQL 的 join 有诸多规则,可能稍有不慎,可能一个不好的 join  语句不仅会导致对某一张表的全表查询,还有可能会影响数据库的缓存,导致大部分热点数据都被替换出去,拖累整个数据库性能。

所以,业界针对 MySQL 的 join 总结了很多规范或者原则,比如说小表驱动大表和禁止三张表以上的 join 操作。下面我们会依次介绍 MySQL  join 的算法,和 Oracle 和 Spark 的 join 实现对比,并在其中穿插解答为什么会形成上述的规范或者原则。

对于 join 操作的实现,大概有 Nested Loop Join (循环嵌套连接),Hash Join(散列连接) 和 Sort Merge  Join(排序归并连接) 三种较为常见的算法,它们各有优缺点和适用条件,接下来我们会依次来介绍。

MySQL 中的 Nested Loop Join 实现

Nested Loop Join 是扫描驱动表,每读出一条记录,就根据 join  的关联字段上的索引去被驱动表中查询对应数据。它适用于被连接的数据子集较小的场景,它也是 MySQL join  的唯一算法实现,关于它的细节我们接下来会详细讲解。

MySQL 中有两个 Nested Loop Join 算法的变种,分别是 Index Nested-Loop Join 和 Block  Nested-Loop Join。

Index Nested-Loop Join 算法

下面,我们先来初始化一下相关的表结构和数据

CREATE TABLE `t1` ( `id` int(11) NOT NULL, `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `a` (`a`) ) ENGINE=InnoDB;  delimiter ;; # 定义存储过程来初始化t1 create procedure init_data() begin declare i int; set i=1; while(i<=10000)do   insert into t1 values(i, i, i);   set i=i+1; end while; end;; delimiter ; # 调用存储过来来初始化t1 call init_data(); # 创建并初始化t2 create table t2 like t1; insert into t2 (select * from t1 where id<=500)

有上述命令可知,这两个表都有一个主键索引 id 和一个索引 a,字段 b 上无索引。存储过程 init_data 往表 t1 里插入了 10000  行数据,在表 t2 里插入的是 500 行数据。

为了避免 MySQL 优化器会自行选择表作为驱动表,影响分析 SQL 语句的执行过程,我们直接使用 straight_join 来让 MySQL  使用固定的连接表顺序进行查询,如下语句中,t1是驱动表,t2是被驱动表。

select * from t2 straight_join t1 on (t2.a=t1.a);

使用我们之前文章介绍的 explain 命令查看一下该语句的执行计划。

如何理解MySQL的join功能

从上图可以看到,t1 表上的 a 字段是由索引的,join 过程中使用了该索引,因此该 SQL 语句的执行流程如下:

  • 从 t2 表中读取一行数据 L1;

  • 使用L1 的 a 字段,去 t1 表中作为条件进行查询;

  • 取出 t1 中满足条件的行, 跟 L1组成相应的行,成为结果集的一部分;

  • 重复执行,直到扫描完 t2 表。

这个流程我们就称之为 Index Nested-Loop Join,简称 NLJ,它对应的流程图如下所示。

如何理解MySQL的join功能

需要注意的是,在第二步中,根据 a  字段去表t1中查询时,使用了索引,所以每次扫描只会扫描一行(从explain结果得出,根据不同的案例场景而变化)。

假设驱动表的行数是N,被驱动表的行数是 M。因为在这个 join  语句执行过程中,驱动表是走全表扫描,而被驱动表则使用了索引,并且驱动表中的每一行数据都要去被驱动表中进行索引查询,所以整个 join 过程的近似复杂度是  N2log2M。显然,N 对扫描行数的影响更大,因此这种情况下应该让小表来做驱动表。

当然,这一切的前提是 join 的关联字段是 a,并且 t1 表的 a 字段上有索引。

如果没有索引时,再用上图的执行流程时,每次到 t1 去匹配的时候,就要做一次全表扫描。这也导致整个过程的时间复杂度编程了 N *  M,这是不可接受的。所以,当没有索引时,MySQL 使用 Block Nested-Loop Join 算法。

Block Nested-Loop Join

Block Nested-Loop Join的算法,简称 BNL,它是 MySQL 在被驱动表上无可用索引时使用的 join  算法,其具体流程如下所示:

  • 把表 t2 的数据读取当前线程的 join_buffer 中,在本篇文章的示例 SQL 没有在 t2 上做任何条件过滤,所以就是讲 t2 整张表  放入内存中;

  • 扫描表 t1,每取出一行数据,就跟 join_buffer 中的数据进行对比,满足 join 条件的,则放入结果集。

比如下面这条 SQL

select * from t2 straight_join t1 on (t2.b=t1.b);

这条语句的 explain 结果如下所示。可以看出

如何理解MySQL的join功能

可以看出,这次 join 过程对 t1 和 t2 都做了一次全表扫描,并且将表 t2 中的 500 条数据全部放入内存 join_buffer  中,并且对于表 t1 中的每一行数据,都要去 join_buffer 中遍历一遍,都要做 500 次对比,所以一共要进行 500 * 10000  次内存对比操作,具体流程如下图所示。

如何理解MySQL的join功能

主要注意的是,第一步中,并不是将表 t2 中的所有数据都放入 join_buffer,而是根据具体的 SQL  语句,而放入不同行的数据和不同的字段。比如下面这条 join 语句则只会将表 t2 中符合 b >= 100 的数据的 b 字段存入  join_buffer。

select t2.b,t1.b from t2 straight_join t1 on (t2.b=t1.b) where t2.b >= 100;

join_buffer 并不是无限大的,由 join_buffer_size 控制,默认值为  256K。当要存入的数据过大时,就只有分段存储了,整个执行过程就变成了:

  • 扫描表 t2,将符合条件的数据行存入 join_buffer,因为其大小有限,存到100行时满了,则执行第二步;

  • 扫描表 t1,每取出一行数据,就跟 join_buffer 中的数据进行对比,满足 join 条件的,则放入结果集;

  • 清空 join_buffer;

  • 再次执行第一步,直到全部数据被扫描完,由于 t2 表中有 500行数据,所以一共重复了 5次

这个流程体现了该算法名称中 Block 的由来,分块去执行 join 操作。因为表 t2 的数据被分成了 5 次存入 join_buffer,导致表 t1  要被全表扫描 5次。

 全部存入分5次存入
内存操作10000 * 50010000 * (100 * 5)
扫描行数10000 + 50010000 *  5 + 500

如上所示,和表数据可以全部存入 join_buffer 相比,内存判断的次数没有变化,都是两张表行数的乘积,也就是 10000 *  500,但是被驱动表会被多次扫描,每多存入一次,被驱动表就要扫描一遍,影响了最终的执行效率。

基于上述两种算法,我们可以得出下面的结论,这也是网上大多数对 MySQL join 语句的规范。

  • 被驱动表上有索引,也就是可以使用Index Nested-Loop Join 算法时,可以使用 join 操作。

  • 无论是Index Nested-Loop Join 算法或者 Block Nested-Loop Join 都要使用小表做驱动表。

因为上述两个 join 算法的时间复杂度至少也和涉及表的行数成一阶关系,并且要花费大量的内存空间,所以阿里开发者规范所说的严格禁止三张表以上的 join  操作也是可以理解的了。

但是上述这两个算法只是 join 的算法之一,还有更加高效的 join 算法,比如 Hash Join 和 Sorted Merged  join。可惜这两个算法 MySQL 的主流版本中目前都不提供,而 Oracle ,PostgreSQL 和 Spark 则都支持,这也是网上吐槽 MySQL  弱爆了的原因(MySQL 8.0 版本支持了 Hash join,但是8.0目前还不是主流版本)。

其实阿里开发者规范也是在从 Oracle 迁移到 MySQL 时,因为 MySQL 的 join 操作性能太差而定下的禁止三张表以上的 join  操作规定的 。

Hash Join 算法

Hash Join 是扫描驱动表,利用 join  的关联字段在内存中建立散列表,然后扫描被驱动表,每读出一行数据,并从散列表中找到与之对应数据。它是大数据集连接操时的常用方式,适用于驱动表的数据量较小,可以放入内存的场景,它对于没有索引的大表和并行查询的场景下能够提供最好的性能。可惜它只适用于等值连接的场景,比如  on a.id = where b.a_id。

还是上述两张表 join 的语句,其执行过程如下

如何理解MySQL的join功能

  • 将驱动表 t2 中符合条件的数据取出,对其每行的 join 字段值进行 hash 操作,然后存入内存中的散列表中;

  • 遍历被驱动表 t1,每取出一行符合条件的数据,也对其 join 字段值进行 hash  操作,拿结果到内存的散列表中查找匹配,如果找到,则成为结果集的一部分。

可以看出,该算法和 Block Nested-Loop Join 有类似之处,只不过是将无序的 Join Buffer 改为了散列表 hash  table,从而让数据匹配不再需要将 join buffer 中的数据全部遍历一遍,而是直接通过 hash,以接近 O(1)  的时间复杂度获得匹配的行,这极大地提高了两张表的 join 速度。

不过由于 hash 的特性,该算法只能适用于等值连接的场景,其他的连接场景均无法使用该算法。

Sorted Merge Join 算法

Sort Merge Join 则是先根据 join  的关联字段将两张表排序(如果已经排序好了,比如字段上有索引则不需要再排序),然后在对两张表进行一次归并操作。如果两表已经被排过序,在执行排序合并连接时不需要再排序了,这时Merge  Join的性能会优于Hash Join。Merge  Join可适于于非等值Join(>,<,>=,<=,但是不包含!=,也即<>)。

需要注意的是,如果连接的字段已经有索引,也就说已经排好序的话,可以直接进行归并操作,但是如果连接的字段没有索引的话,则它的执行过程如下图所示。

如何理解MySQL的join功能

  • 遍历表 t2,将符合条件的数据读取出来,按照连接字段 a 的值进行排序;

  • 遍历表 t1,将符合条件的数据读取出来,也按照连接字段 a 的值进行排序;

  • 将两个排序好的数据进行归并操作,得出结果集。

Sorted Merge Join 算法的主要时间消耗在于对两个表的排序操作,所以如果两个表已经按照连接字段排序过了,该算法甚至比 Hash Join  算法还要快。在一边情况下,该算法是比 Nested Loop Join 算法要快的。

下面,我们来总结一下上述三种算法的区别和优缺点。

 Nested Loop JoinHash JoinSorted Merge Join
连接条件适用于任何条件只适用于等值连接(=)等值或非等值连接(>,<,=,>=,<=),&lsquo;<>&rsquo;除外
主要消耗资源CPU、磁盘I/O内存、临时空间内存、临时空间
特点当有高选择性索引或进行限制性搜索时效率比较高,能够快速返回第一次的搜索结果当缺乏索引或者索引条件模糊时,Hash Join 比 Nested Loop 有效。通常比 Merge Join 快。在数据仓库环境下,如果表的纪录数多,效率高当缺乏索引或者索引条件模糊时,Sort Merge Join 比 Nested Loop 有效。当连接字段有索引或者提前排好序时,比 hash join 快,并且支持更多的连接条件
缺点无索引或者表记录多时效率低建立哈希表需要大量内存,第一次的结果返回较慢所有的表都需要排序。它为最优化的吞吐量而设计,并且在结果没有全部找到前不返回数据
需要索引是(没有索引效率太差)

连接条件 适用于任何条件 只适用于等值连接(=) 等值或非等值连接(>,<,=,>=,<=),&lsquo;<>&rsquo;除外

主要消耗资源 CPU、磁盘I/O 内存、临时空间 内存、临时空间

特点 当有高选择性索引或进行限制性搜索时效率比较高,能够快速返回第一次的搜索结果 当缺乏索引或者索引条件模糊时,Hash Join 比 Nested  Loop 有效。通常比 Merge Join 快。在数据仓库环境下,如果表的纪录数多,效率高 当缺乏索引或者索引条件模糊时,Sort Merge Join 比  Nested Loop 有效。当连接字段有索引或者提前排好序时,比 hash join 快,并且支持更多的连接条件

缺点 无索引或者表记录多时效率低 建立哈希表需要大量内存,第一次的结果返回较慢  所有的表都需要排序。它为最优化的吞吐量而设计,并且在结果没有全部找到前不返回数据

需要索引 是(没有索引效率太差) 否 否

对于 Join 操作的理解

讲完了 Join 相关的算法,我们这里也聊一聊对于 join 操作的业务理解。

在业务不复杂的情况下,大多数join并不是无可替代。比如订单记录里一般只有订单用户的  user_id,返回信息时需要取得用户姓名,可能的实现方案有如下几种:

  • 一次数据库操作,使用 join 操作,订单表和用户表进行 join,连同用户名一起返回;

  • 两次数据库操作,分两次查询,第一次获得订单信息和 user_id,第二次根据 user_id 取姓名,使用代码程序进行信息合并;

  • 使用冗余用户名称或者从 ES 等非关系数据库中读取。

上述方案都能解决数据聚合的问题,而且基于程序代码来处理,比数据库 join 更容易调试和优化,比如取用户姓名不从数据库中取,而是先从缓存中查找。

当然, join  操作也不是一无是处,所以技术都有其使用场景,上边这些方案或者规则都是互联网开发团队总结出来的,适用于高并发、轻写重读、分布式、业务逻辑简单的情况,这些场景一般对数据的一致性要求都不高,甚至允许脏读。

但是,在金融银行或者财务等企业应用场景,join  操作则是不可或缺的,这些应用一般都是低并发、频繁复杂数据写入、CPU密集而非io密集,主要业务逻辑通过数据库处理甚至包含大量存储过程、对一致性与完整性要求很高的系统。

感谢各位的阅读,以上就是“如何理解MySQL的join功能”的内容了,经过本文的学习后,相信大家对如何理解MySQL的join功能这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

您可能感兴趣的文档:

--结束END--

本文标题: 如何理解MySQL的join功能

本文链接: https://lsjlt.com/news/61978.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 如何理解MySQL的join功能
    这篇文章主要讲解了“如何理解MySQL的join功能”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解MySQL的join功能”吧!正文在日常数据库查询...
    99+
    2024-04-02
  • MySQL的 join功能有什么用
    小编给大家分享一下MySQL的 join功能有什么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!关于MySQL 的 join,...
    99+
    2024-04-02
  • 如何理解ADO.NET的功能
    本篇文章给大家分享的是有关如何理解ADO.NET的功能,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。首先,我认为ADO.NET是一个很有用的组件,如果继续得到改进和增强,我想该...
    99+
    2023-06-17
  • 解析MySQL join查询的原理
    MySQL用Nested-Loop Join算法实现join查询 区分驱动表和被驱动表,以驱动表的结果集为循环的基础,访问被驱动表过滤数据,然后合并结果,驱动表在外循环、被驱动表在内...
    99+
    2024-04-02
  • MySQL的join buffer原理
    一、MySQL的join buffer 在MySQL对于join操作的处理过程中,join buffer是一个重要的概念,也是MySQL对于table join的一个重要的优化手段。虽然这个概念实现并不复杂,但是这...
    99+
    2022-05-19
    MySQL join buffer
  • mysql如何优化join
    本篇文章为大家展示了mysql如何优化join,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。 mysql 在join 表的时候相当类型的...
    99+
    2024-04-02
  • mysql中inner join和left join如何使用
    这篇文章主要介绍“mysql中inner join和left join如何使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“mysql中inner join和left&...
    99+
    2023-07-05
  • MySql中如何使用JOIN
    这篇文章将为大家详细讲解有关MySql中如何使用JOIN,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。JOIN的含义就如英文单词“join”一样,连接两张表,大致分为内连接,外连接,右连接,左连接,自然连...
    99+
    2023-06-29
  • 我们如何区分 MySQL CROSS JOIN 和 INNER JOIN?
    我们只能根据连接谓词(即指定的条件)来区分 MySQL CROSS JOIN 和 INNER JOIN。在编写 INNER JOIN 查询时,我们需要指定条件,但相反,在编写 CROSS JOIN 查询时,我们不需要指定条件。为了理解它,我...
    99+
    2023-10-22
  • 一文详解MySQL Join使用原理
    目录Join的类型Join原理Simpe Nested-Loop JoinIndex Nested-Loop Joinblock Nested-Loop JoinJoin优化Join的类型 left join,以左表为驱...
    99+
    2023-04-25
    MySQL Join原理 MySQL Join
  • 一文详解MySQL Join使用原理
    目录Join的类型Join原理Simpe Nested-Loop JoinIndex Nested-Loop JoinBlock Nested-Loop JoinJoin优化Join...
    99+
    2023-05-17
    MySQL Join原理 MySQL Join
  • 如何理解ajax完整功能框架
    本篇内容主要讲解“如何理解ajax完整功能框架”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何理解ajax完整功能框架”吧!记得有篇文章数落AJAX的&quo...
    99+
    2024-04-02
  • 如何理解.NET Framework 4.0功能特点
    如何理解.NET Framework 4.0功能特点,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。生存在这个时代的开发人员是又幸福又忙碌。因为他们会因为一个新工具的发布获得...
    99+
    2023-06-17
  • 如何深入解析MySQL分区Partition功能
    这篇文章将为大家详细讲解有关如何深入解析MySQL分区Partition功能,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。 自5...
    99+
    2024-04-02
  • MySQL中join语句如何优化
    今天小编给大家分享一下MySQL中join语句如何优化的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Simple Neste...
    99+
    2023-07-05
  • MySQL之join查询如何优化
    这篇文章主要介绍“MySQL之join查询如何优化”,在日常操作中,相信很多人在MySQL之join查询如何优化问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”MySQL之join查询如何优化”的疑惑有所帮助!...
    99+
    2023-07-05
  • 如何解析SQLServer2005的Top功能
    如何解析SQLServer2005的Top功能,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。所有人都知道select top 的用法...
    99+
    2024-04-02
  • 如何理解C++编译器编译功能
    如何理解C++编译器编译功能,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。下面深度讲解C++中的大规模C++编译器,C++编译器具有很强的复杂性,并且源程序的行数也是非常多...
    99+
    2023-06-17
  • 详解如何使用Python操作MySQL的各种功能
    目录连接mysql增删改查插入数据删除数据更新数据查询数据批量操作批量插入数据批量删除数据批量更新数据当今互联网时代,数据处理已经成为了一个非常重要的任务。而MySQL作为一款开源的关系型数据库,被广泛应用于各种场景。本...
    99+
    2023-04-19
    Python操作MySQL功能 Python操作MySQL Python MySQL
  • MySQL多表查询内连接外连接详解,join、left join、right join、full join的使用
    目录 1、多表查询概览 1.1、分类 1.2、外连接的分类 1.3、常用的SQL语法标准 2、内外联接案例 2.1、初始化表 2.2、内连接 2.3、外连接案例 2.4、全连接案例 2.5、union和union all...
    99+
    2023-09-02
    mysql 数据库 java
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作