MySQL中联合查询的一些优化感悟


MySQL中联合查询的一些优化感悟

背景

今天博主在left join的SQL语句优化中发现了一个奇怪的现象,随后在网上查阅了相关资料,现在复盘并记录下来。

描述

现在有 data_users 以及 data_posts 两张表。

data_users 表中有主键索引iddata_posts 表中索引 uid,uid是外键。

现在有如下SQL语句:

select * from data_users LEFT JOIN data_posts on data_users.id = data_posts.uid;

sql结果

可以看到上面结果,左表是没有命中索引的,所以进行了全表扫描。右表因为 uid 建立了索引,最后命中了索引,但是这样的效率其实比较低的,特别是在两张表数据量规模大的时候,他所扫描的基本是是左表的总行数和右表的查询结果的笛卡尔积。

接下来我们改下sql语句。

select * from data_users JOIN data_posts on data_users.id = data_posts.uid;

sql结果2

可以看到上面结果都命中了索引,这样效率得到了大大的提升。

那么除了这种改动之外,我对第一个sql语句中做另外的改变,得到第二个优化后的语句。

select * from data_users LEFT JOIN data_posts on data_users.id = data_posts.uid where data_users.id = 1;

sql结果3

可以看到上面的查询结果也全都命中了索引,那么这其中的原理究竟为何呢?

MySQL中join的原理

在网上查询了mysql实现join的原理,原来 MySQL 内部采用了一种叫做 nested loop join的算法,该算法就是通过驱动表的结果集作为循环基础数据,然后一条一条的通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果。如果还有第三个参与了join,则再通过前两个表的结果集作为循环基础数据,再一次通过循环查询条件到第三个表中查询数据,如此往复,基本上Mysql采取容易理解的算法来实现join。所以驱动表的选择非常重要,驱动表的数据量小可以显著降低扫描的行数。

那么为什么一版情况下join的效率要高于left join很多呢,其实一般情况下参与联合查询的两张表都会一大一小,如果是join,在没有其他过滤条件的情况下,会选择用小表作为驱动表,但是left join一般因为本身的特性决定了会用大表做驱动表,这样下来效率就差了不少。

结束语

在了解join的基本原理后,在我们使用join后,mysql会使用 data_posts的结果集作为驱动表,而该结果集已经命中了索引,因此效率得到了显著的提升。

而第二个优化后的sql语句,因为加了where子句,虽然因为left join的特性,选择了大表 data_users作为驱动表,但本身经过过滤后,数量大大减少且命中了索引。因此该语句的效率也得到了显著的提升。

参考资料: mysql join 和left join 对于索引的问题


文章作者: 我若为侠
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 我若为侠 !
 上一篇
消息队列之RabbitMQ(二) Laravel中使用rabbitmq 消息队列之RabbitMQ(二) Laravel中使用rabbitmq
消息队列之RabbitMQ(二) Laravel中使用rabbitmq简介博主在之前的文章大致介绍了下 rabbitmq 的原理、应用场景。接下来讲下如何在 laravel5.5 中去使用。 安装rabbitmq在本地进行测试的时候,使用
下一篇 
消息队列之RabbitMQ(一) 介绍、核心概念、交换器类型 消息队列之RabbitMQ(一) 介绍、核心概念、交换器类型
消息队列之RabbitMQ(一) 介绍、核心概念、交换器类型背景博主在介绍 RabbitMQ 之前有必要说说消息队列是什么? 消息是指在应用间传送的数据。消息可以非常简单,比如只包含文本字符串,也可以很复杂,可能包含嵌入对象。 消息队列是一
  目录