听说mysql中的join很慢?是你用的姿势不对吧

目录
  • 驱动表与被驱动表
    • 驱动表和被驱动表有什么差异
      • 基于索引的join
      • join查询中如何选择驱动表
      • 不使用join,执行效率是否会更高

join 是进行两个或多个数据表进行关联查询的过程中,经常使用的一种查询手段。提到join,你一定会想到"笛卡尔积",当数据量很大的时候,"笛卡尔积"运算量会成倍的增加,在我们的印象中,join是一种运算效率不高的查询语句。

除了定性的判断join慢之外,你能定量的判断join的执行效率吗?

经过下面对join执行效率定量分析后,可能你会改变对join的认识,不在想当然的认为join就一定很慢了。

驱动表与被驱动表

进行join操作的两个表,分别称为驱动表和被驱动表,到底哪个是驱动表,哪个是被驱动表是不确定的,这个是mysql优化器来决定,和sql语句中两个表的位置没有关系。

如果我们想要强制指定两个表的对应关系,可以将sql中的join替换成 straight_join,替换后,在straight_join前的表称为驱动表,在straight_join后的表,称为被驱动表。

驱动表和被驱动表有什么差异

在join语句执行的过程中,驱动表和被驱动表所执行的操作是不同的。同是驱动表或被驱动表,在不同的join类型中,所执行操作也是不同的。

下面我们分析一下,不同join类型下,驱动表和被驱动表所做的操作的具体内容。

为了方便下面问题的讨论,我们建立如下的表结构:

create table 'table1' (
'id' int(11) NOT NULL,
'a' int(11) DEFAULT NULL,
'b' int(11) DEFAULT NULL,
PRIMARY KEY ('id'),
KEY 'a' ('a')
) engine = Innodb;

insert into table1 values(1,1,1)
insert into table1 values(2,2,2)
...
insert into table1 values(1000,1000,1000) // 也可以使用存储过程来实现大批量数据的插入

create table table2 like table1;
insert into t2 (select * from t2 where id <= 100)

建立表结构完全相同的两个表table1和table2,共有三个字段:id为主键字段,索引字段a和普通字段b。向table1中插入了1000行自增的数据,将table1中的前100行数据插入到table2中。

基于索引的join

如果在join过程中,使用到了索引,这种join又被称为 Index Nested-Loop Join(NLJ)。

如下面这个语句:

select * from table2  straight_join table1 on table2.a = table1.a;

为了便于明确驱动表和被驱动表,我们使用 straight_join 代替 join,这样就可以明确 table2 为驱动表,table1为被驱动表。

因为在被驱动表 table1上有索引a字段,在join的时候,会使用到这个索引,具体可以通过查看上面sql的执行计划:

explain select * from table2  straight_join table1 on table2.a = table1.a;

执行计划图:

该条语句的执行过程如下:

1.从table2中,读入一行R。

2.从该数据行R中取出字段a,到table1中去查找满足a=$R.a的数据行,因为在table1表中,字段a上有索引,所以这个查询效率很高。

3.将从2中查询返回的结果和R,构成结果集中一行。

4.重复步骤1到3,直到遍历完table2中的所有数据行。

这个过程遍历 table2中的所有数据行,取出每一行中的a值,然后去table1中查找满足条件的数据行,将table1中满足条件的数据和table2中遍历到的数据,组合成结果集中的数据。

在整个过程中:

驱动表table2所做的操作:被逐行遍历,也就是进行全表扫描,该过程要扫描100行数据。

被驱动表table1所做的操作:基于索引字段进行数据查询,因为table1中,没有a值相同的两行数据,所以每次搜索过程只会扫描一行数据。因为table2中有100行数,所以在table1中要执行100次搜索过程,也就是在table1中,也要扫描100行数据。

所以这个join语句整个执行下来 要扫描200行数。

如果让 table1作为驱动表,table2作为被驱动表的话,执行语句如下:

select * from table1  straight_join table2 on table2.a = table1.a;

和前者有和区别呢?

根据上面的分析,驱动表需要进行全表扫描,被驱动表基于索引字段进行数据搜索。

table1作为驱动表时,sql语句执行计划如下图:

当 table1作为驱动表,table2作为被驱动表时:

驱动表table1需要被扫描 1000行。被驱动表table2需要进行 1000次搜索,但是最终只能成功搜索到100行数据。总的所有数据行数1100行。

这样对比下来,table2作为驱动表,table1作为被驱动表执行的效率,要比table1作为驱动表,table2作为被驱动表的执行效率要高一些。

join查询中如何选择驱动表

除了分析扫描行数,我们可以对NLJ执行过程中,总的时间复杂度计算一下,看一下哪个因素对join查询效率影响比较大,进而来对我们选择驱动表提供参考。

我们假设驱动表中的数据行数是N,被驱动表中的数据行数为M,因为在被驱动表中查询一行数据,要先搜索普通索引a,然后再回表到主键索引,才能获取完整的一行数据。

表中数据行数为M,通过主键索引树和普通索引树查找一行数据的时间复杂度都是log2M,所以查找一行数据的时间复杂度为2*log2M。驱动表中有N行数,因此驱动表要扫描N行,驱动表中的每行数据都要到被驱动表中进行一次搜索。所以当驱动表数据行数为N,被驱动表数据行数为M的情况下,一次基于索引的join查询的近似时间复杂度为 O = N + N*2*log2M。

整个join语句的时间复杂度,与驱动表中行数的关系为: O = (1+2*log2M)*N ,是线性关系。和被驱动表中行数的关系为:O = N*2*log2M +N 是对数函数关系。

基于数学知识,我们知道 "驱动表中行数"对整个sql执行时间复杂度的影响 要比"被驱动表中行数" 影响要大。因此在 基于索引的join(NLJ)中,我们应该尽量使用 数据量小的表作为驱动表。这样可以减少扫描的行数,以及整体的时间复杂度。

不使用join,执行效率是否会更高

如果不使用join的情况下,要想实现下图类似功能,

select * from table2  join table1 on table2.a = table1.a;

我们需要把 table2中的数据全部取出来,

select * from table2; // 扫描100行数据

共100行数据,然后循环遍历这100行数据,取出每行数据中的a值$R.a,去执行

select * from table1 where a = $R.a // 扫描1行数据

把该条语句返回的结果 和R拼接在一起,构成结果集中的一行数据。

这种不使用join的方式,也会扫描200行数据,只不过要执行的sql语句会有101条,而使用join语句的情况下,却只有1条。相比使用join,不使用join,会增加100次与mysql的交互过程,整体的执行效率相比使用join反而更低。

由此可见,在被驱动表上可以使用到索引的情况下,join操作的效率还是比较高的。读到这里,你是否会改变对join的认识呢?还会想当然的认为join执行效率很低吗?

可能你会问,如果join的过程中,被驱动表上没有索引呢?的确,当被驱动表上没有索引的情况下,join的执行效率会变慢很多,显然,"join执行的效率低"这个认知,不是空穴来风,但是变慢的原因是什么呢?感兴趣的老铁可以看一下,本篇文章。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • MYSQL Left Join优化(10秒优化到20毫秒内)

    目录 [功能背景] [原始的SQL] [原始的SQL分析] [分析步骤] [优化后的SQL] [优化的SQL分析] 结合工作中的内容和大家分享一次Left Jon优化的过程,希望能给同学们新的思路. [功能背景]     我们需要按照用户订单号和商户号统计出购买的商品数量和售后的商品数量.涉及到的表和关系见下图: 很不幸工程师在起初进行表结构设计的时候没有在商户订单表中记录下购买的商品总数,在商户订单的售后单中也没记录下售后的商品数量. [原始的SQL] select o.no,s_order.

  • MySQL中(JOIN/ORDER BY)语句的查询过程及优化方法

    在MySQL查询语句过程和EXPLAIN语句基本概念及其优化中介绍了EXPLAIN语句,并举了一个慢查询例子: 可以看到上述的查询需要检查1万多记录,并且使用了临时表和filesort排序,这样的查询在用户数快速增长后将成为噩梦. 在优化这个语句之前,我们先了解下SQL查询的基本执行过程: 1.应用通过MySQL API把查询命令发送给MySQL服务器,然后被解析 2.检查权限.MySQL optimizer进行优化,经过解析和优化后的查询命令被编译为CPU可运行的二进制形式的查询计划(quer

  • MySQL JOIN关联查询的原理及优化

    目录 1 关联查询的执行 2 没有索引的算法 1 关联查询的执行 关联查询的执行过程是:先遍历关联表t1(驱动表,全表扫描),然后根据从表t1中取出的每行数据中的a值,去表t2(被关联表,被驱动表)中查找满足条件的记录,可以走t2的索引搜索.在形式上,这个过程就跟我们写程序时的嵌套查询类似,并且可以用上被驱动表的索引,所以我们称之为“Index Nested-Loop Join”,简称NLJ.在join语句的执行流程中,驱动表是走全表扫描,而被驱动表是走索引树搜索. 假设被驱动表的行数是M.每次

  • 总结12个MySQL慢查询的原因分析

    目录 1. SQL 没加索引 2. SQL 索引不生效 2.1 隐式的类型转换,索引失效 2.2 查询条件包含 or,可能导致索引失效 2.3. like 通配符可能导致索引失效 2.5 在索引列上使用 mysql 的内置函数 2.6 对索引进行列运算(如,+.-.*./), 索引不生效 2.7 索引字段上使用(!= 或者 < >),索引可能失效 2.8 索引字段上使用 is null, is not null,索引可能失效 2.9 左右连接,关联的字段编码格式不一样 2.10 优化器选错了索

  • 听说mysql中的join很慢?是你用的姿势不对吧

    目录 驱动表与被驱动表 驱动表和被驱动表有什么差异 基于索引的join join查询中如何选择驱动表 不使用join,执行效率是否会更高 join 是进行两个或多个数据表进行关联查询的过程中,经常使用的一种查询手段.提到join,你一定会想到"笛卡尔积",当数据量很大的时候,"笛卡尔积"运算量会成倍的增加,在我们的印象中,join是一种运算效率不高的查询语句. 除了定性的判断join慢之外,你能定量的判断join的执行效率吗? 经过下面对join执行效率定量分析后,

  • 在MySQL中使用JOIN语句进行连接操作的详细教程

    到目前,我们已经学习了从一个表中获取数据.这是简单的需要,但在大多数现实MySQL的使用,经常需要将数据从多个表中的一个单一的查询. 可以使用多个表中的单一SQL查询.在MySQL中联接(join)行为是指两个或多个表到一个表中可以使用连接在SELECT,UPDATE和DELETE语句中加入MySQL表.我们将看到一个例子LEFT JOIN简单的MySQL连接. 在命令提示符使用联接: 假设我们两个表的教程tcount_tbl和tutorials_tbl的完整列表如下: 例子: 试试下面的例子:

  • mysql中left join设置条件在on与where时的用法区别分析

    本文实例讲述了mysql中left join设置条件在on与where时的用法区别.分享给大家供大家参考,具体如下: 一.首先我们准备两张表来进行测试. CREATE TABLE `a` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `name` varchar(32) DEFAULT '' COMMENT '名称', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET

  • mysql中inner join和left join使用详解

    目录 区别 inner join 场景 inner join 场景 区别 返回不同1.inner join只返回两个表中联结字段相等的行2.left join的数量小于等于左表和右表中的记录数量. 数量不同1.inner join返回包括左表中的所有记录和右表中联结字段相等的记录.2.left join的数量以左表中的记录数量相同 记录属性不同1.inner join不足的记录属性会被直接舍弃2.left join不足的记录属性用NULL填充 inner join 场景 设计两张表: chann

  • MySQL中的join以及on条件的用法解析

    目录 join的用法:连接 on的用法:添加约束 在数据库中验证 MySQL中连接表时join和on的区别 MySQL中 join的用法:join具有 连接的作用,即当两个或者两个以上的表有关系时,需要用join来连接这些相关的表,来处理或分析数据: join的用法:连接 举例先看看,就懂了: 例如: 有一个表叫做stu,一个表叫class,stu join class会生成一个新的表, 我们执行: stu join class 后,新的表成为: 通过上面的例子总结一下: join后的列名是两个

  • MySQL中Nested-Loop Join算法小结

    不知不觉的玩了两年多的MySQL,发现很多人都说MySQL对比Oracle来说,优化器做的比较差,其实某种程度上来说确实是这样,但是毕竟MySQL才到5.7版本,Oracle都已经发展到12c了,今天我就看了看MySQL的连接算法,嗯,现在来说还是不支持Hash Join,只有Nested-Loop Join,那今天就总结一下我学习的心得吧. Nested-Loop Join基本算法实现,伪代码是这样: for each row in t1 matching range { for each r

  • Mysql中的join操作

     join的类型 1.  内联结:将两个表中存在联结关系的字段符合联结关系的那些记录形成记录集的联结. 2.  外联结:分为外左联结和外右联结. 案例背景 create table java (name varchar(255)); insert into java values ('java1'),('java2'),('blue'); create table mysql (name varchar(255)); insert into mysql values ('mysql1'),('m

  • mysql中的join和where优先级顺序解读

    目录 mysql 的 join 和 where 优先级 定义 测试数据表 查询 sql 及结果 总结 mysql 的 join 和 where 优先级 定义 join功能 inner join(内连接,或等值连接) : 获取两个表中字段匹配关系的记录. left join (左连接) : 获取左表所有记录,即使右表没有对应匹配的记录. right join (右连接) : 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录. where where 是 mysql 语

  • MySQL中join语句的基本使用教程及其字段对性能的影响

    join语句的基本使用 SQL(MySQL) JOIN 用于根据两个或多个表中的字段之间的关系,从这些表中得到数据. JOIN 通常与 ON 关键字搭配使用,基本语法如下: ... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON conditiona table1 通常称为左表,table2 称为右表.ON 关键字用于设定匹配条件,用于限定在结果集合中想要哪些行.如果需要指定其他条件,后面可以加上 WHERE 条件 或者 LIMIT 以限制记录返回数目等.

随机推荐