透彻理解Mysql连接的原理

 更新时间:2023年07月28日 09:18:46   作者:Zeran  
这篇文章主要为大家介绍了Mysql连接原理的深入理解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

表结构

t1

CREATE TABLE `t1` (
  `m1` int(11) DEFAULT NULL,
  `n1` char(1) COLLATE utf8mb4_bin DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
INSERT INTO `t1`(`m1`, `n1`) VALUES (1, 'a'),(2, 'b'),(3, 'c');

t2

CREATE TABLE `t2` (
  `m2` int(11) DEFAULT NULL,
  `n2` char(1) COLLATE utf8mb4_bin DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
INSERT INTO `t2`(`m2`, `n2`) VALUES (2, 'b'),(3, 'c'),(4, 'd');

嵌套循环连接

对于两张表的连接来说,驱动表只会被访问一遍,但被驱动表却要被访问好多遍;具体访问几遍取决于对驱动表执行单表查询后的结果集中有多少条记录。对于内连接来说选取哪个表作为驱动表都没关系;而外连接的驱动表是固定的。对于t1表和t2表执行内链接的大致过程如下:

  • 步骤1. 选取驱动表,适用于驱动表相关的过滤条件,选取代价最低的单表访问方法来执行对驱动表的单表查询。
  • 步骤2. 对步骤1中查询驱动表得到的结果集中的每一条记录,都分别到被驱动表中查找匹配到记录。

如果有3个表进行连接,那么步骤2中得到的结果集就像是新的驱动表,然后第三个表就成为了被驱动表,然后重复上面的过程。也就是针对步骤2中得到的结果集中的每一条记录都需要到t3表中找一找有没有匹配到记录。

这个过程就像是一个嵌套的循环,所以这种 “驱动表只访问一次,但被驱动表却可能访问多次,且访问次数取决于对驱动表执行单表查询后的结果集中有多少条记录” 的连接执行方式称为嵌套循环连接(Nested-Loop Join),这是最简单也是最笨拙的一种连接查询算法。

需要注意的是,对于嵌套循环连接算法来说,每当我们从驱动表中得到一条记录时,就根据这条记录立即到被驱动表中查询一次,如果得到了匹配到记录,就把组合后到记录发送给客户端,然后再到驱动表中获取下一条记录;这个过程将重复进行。上面说到的 “结果集“ 是一个抽象的概念,并不是把驱动表中所有的记录都先查出来放到某个地方(比如内存或者磁盘中),然后再遍历被驱动表。

使用索引加快连接速度

我们知道,在嵌套循环连接中肯能需要访问多次被驱动表。如果访问被驱动表的方式都是全表扫描,那得要扫描好多次!查询t2表其实就相当于一次单表查询,我们可以利用索引来加快查询速度。如下内链接的例子:

select * from t1, t2 where t1.m1 > 1 and t1.m1 = t2.m2 and t2.n2 < 'd';

这个连接查询使用的其实是嵌套循环连接算法,首先查询驱动表t1后的结果集中有2条记录,嵌套循环连接算法需要查询被驱动表两次:

当t1.m1=2 时,查询一遍t2表,对t2表的查询语句相当于:

select * from t2 where t2.m2 = 2 and t2.n2 < 'd';

当t1.m1=3 时,查询一遍t2表,对t2表的查询语句相当于:

select * from t2 where t2.m2 = 3 and t2.n2 < 'd';

可以看到,原来的t1.m1=t2.m2这个涉及两个表的过滤条件在针对t2表进行查询时,关于t1表的条件就已经确定了,所以我们只需要单单优化针对t2表的查询即可。上述两个对t2表的查询语句中利用到的是m2和n2列,我们可以进行如下尝试。

  • 在m2列上建立索引。因为针对m2列的条件是等值查找,比如t2.m2=2、t2.m2=3等,所以可能使用到ref访问方法。假设使用ref访问方法来执行对t2表的查询,需要在回表之后再判断t2.n2<'d'这个条件是否成立。
    这里有一个比较特殊的情况,假设m2列是t2表的主键,或者是不允许存储null值的唯一二级索引列,那么使用 "t2.m2=常数值" 这样的条件从t2表中查找记录时,代价就是常数级别的。我们知道,在党表中使用主键值或者唯一二级索引列的值进行等值查找的方式称为const,而在连接查询中对被驱动表的主键或者不允许存储null值的唯一二级索引进行等值查找使用的访问方法就称为eq_ref。
  • 在n2列上建立索引,涉及的条件是t2.n2<'d',可能用到range访问方法。假设使用range访问方法对t2表进行查询,需要在回表之后再判断包含m2列的条件是否成立。
    假设m2列和n2列上都存在索引,那么就需要从这两个里面挑一个代价更低的索引来查询t2表。
    另外,连接查询的查询列表和过滤条件中有时可能只涉及被驱动表的部分列,而这些列都是某个二级索引的一部分,在这种情况下不能使用eq_ref、ref、ref_or_null或者range等访问方法来查询被驱动表,也可以通过扫描全部二级索引记录(即使用index访问方法)来查询被驱动表。所以建议最好不要使用 * 作为查询列表,而是把真正用到的列作为查询列表。

基于块的嵌套循环连接

由于现实生活中的表可不像t1、t2这样只有3条记录,成千上万条记录都是少的,几百万、几千万甚至几亿条记录到处都是。现在假设我们不能使用索引加快被驱动表的查询过程,所以对于驱动表结果集中的每一条记录,都需要对被驱动表执行全表扫描。这样在对被驱动表进行全表扫描时,可能前面的记录还在内存中,而表后面的记录还在磁盘上。而等到扫描表中后面的记录时,有可能由于内存不足,需要把表前面的记录从内存中释放掉给现在正在扫描的记录腾地方。前面强调过,在采用嵌套循环连接算法的两表连接过程中,被驱动表可是要访问好多次。如果这个被驱动表中的数据特别多而且不能使用索引进行访问,那就相当于要从磁盘上读这个表好多次,这个I/O的代价就太大了。所以我们得想想办法,尽量减少被驱动表的访问次数。

通过上面的叙述我们了解到,驱动表结果集中有多少条记录,就可能把被驱动表从磁盘加载到内存多少次。我们是否可以在把被驱动表中的记录加载到内存时,一次性地与驱动表中的多条记录进行匹配呢?这样就可以大大减少重复从磁盘上加载被驱动表的代价了。所以设计MySql的大叔提出了一个名为Join Buffer(连接缓冲区)的概念。Join Buffer就是在执行连接查询前申请的一块固定大小的内存。先把若干条驱动表结果集中的记录装在这个Join Buffer中,然后开始扫描被驱动的表,每一条被驱动表的记录一次性地与Join Buffer中的多条驱动表记录进行匹配。由于匹配的过程都是在内存中完成的,所以这样可以显著减少被驱动表的I/O代价。

最好的情况是Join Buffer足够大,能容纳驱动表结果集中的所有记录,这样只需要访问一次被驱动表就可以完成连接操作了。设计MySql的大叔把这种加入了Join Buffer的嵌套循环连接算法称为基于块的嵌套循环连接(Block Nested-Loop Join)算法。

这个Join Buffer的大小可以通过启动选项或者系统变量join_buffer_size进行配置,默认大小为262144字节(256KB),最小可以设置为128字节。当然,在我们优化对被驱动表的查询时,最好是为驱动表加上高效的索引。如果实在不能使用索引,并且自己机器的内存也比较大,则可以尝试调大join_buffer_size的值来对连接查询进行优化。

另外需要注意的是,Join Buffer中并不会存放驱动表记录的所有列,只有查询列表中的列和过滤条件中的列才会被放到Join Buffer中,所以这也再次提醒我们,最好不要把 * 作为查询列表,只需要把关心的列放到查询列表中就好了;这样还可以在Join Buffer中放置更多的记录。

以上就是透彻理解Mysql连接的原理的详细内容,更多关于Mysql连接原理的资料请关注脚本之家其它相关文章!

相关文章

  • mysql alter添加列的实现方式

    mysql alter添加列的实现方式

    这篇文章主要介绍了mysql alter添加列的实现方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-01-01
  • mysql.help_topic的作用以及使用方法

    mysql.help_topic的作用以及使用方法

    这篇文章主要给大家介绍了关于mysql.help_topic的作用以及使用方法,mysql.help_topic表是MySQL数据库中的一个系统表,它存储了MySQL数据库中所有可用的帮助主题的信息,需要的朋友可以参考下
    2023-11-11
  • MySQL如何使用视图插入修改删除数据

    MySQL如何使用视图插入修改删除数据

    视图除了进行查询记录外,也可以利用视图进行插入、更新、删除记录的操作,减少对基表中信息的直接操作,提高了数据的安全性,下面这篇文章主要给大家介绍了关于MySQL如何使用视图插入修改删除数据的相关资料,需要的朋友可以参考下
    2023-06-06
  • springboot启动404问题以及解决方案

    springboot启动404问题以及解决方案

    这篇文章主要介绍了springboot启动404问题以及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • 关于case when语句的报错问题详解

    关于case when语句的报错问题详解

    SQL CASE 表达式是一种通用的条件表达式,类似于其它语言中的 if/else 语句,下面这篇文章主要给大家介绍了关于case when语句的报错问题的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2018-08-08
  • MySQL中BIGINT数据类型如何存储整数值

    MySQL中BIGINT数据类型如何存储整数值

    mysql数据库设计,其中对于数据性能优化,字段类型考虑很重要,下面这篇文章主要给大家介绍了关于MySQL中BIGINT数据类型如何存储整数值的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-10-10
  • 批量替换 MySQL 指定字段中的字符串

    批量替换 MySQL 指定字段中的字符串

    批量替换 MySQL 指定字段中的字符串是数据库应用中很常见的需求,但是有很多初学者在遇到这种需求时,通常都是用脚本来实现;其实,MySQL 内置的有批量替换语法,效率也会高很多;想了解具体方法,继续阅读本文吧 :)
    2009-09-09
  • MYSQL实现排名及查询指定用户排名功能(并列排名功能)实例代码

    MYSQL实现排名及查询指定用户排名功能(并列排名功能)实例代码

    这篇文章主要给大家介绍了关于MYSQL实现排名及查询指定用户排名功能(并列排名功能)的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧
    2018-10-10
  • mysql 无法连接问题的定位和修复过程分享

    mysql 无法连接问题的定位和修复过程分享

    开发的一款网站防护产品中出现了一个客户端上安装后Mysql每隔一段时间就出现问题,这个问题是客户反馈的,所以需要去复现和定位
    2013-03-03
  • MySQL中索引与视图的用法与区别详解

    MySQL中索引与视图的用法与区别详解

    索引与视图是我们在日常使用mysql必不可少的一部分,最近在学习中看到一本书中关于这方法写的不错,所以这篇文章主要给大家介绍了关于MySQL中索引与视图的使用与区别的相关资料,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2017-11-11

最新评论