MySQL是如何优化in子查询的?

目录

前言

一、普通in子查询

二、物化表

三、SQL优化 

四、IN语句的优化方式

1. 使用子查询代替IN查询

2. 使用JOIN代替IN查询

3. 使用EXISTS代替IN查询

4. 使用索引优化IN查询

5. 优化查询语句

总结


前言

对于很多的开发小伙伴来说,在MySQL中进行in子查询是一个非常常见的操作。

虽然也有很多人说,尽量少用in子查询,in的数量过多会影响查询性能。

但其实MySQL做了不少的优化手段来保证in子查询的性能,大家也能在实际的业务中感受到in子查询的速度也没那么慢。

那今天就带大家了解一下,MySQL到底是怎么来优化in子查询的。


一、普通in子查询

首先,我们看一下MySQL是如何执行一个普通的in子查询的。

以一个简单的子查询为例:

select  * from user where name in ('b','f','g')

用户表B+Tree 树的数据结构,name为二级索引。

 

在这个图里,最上层的是根节点,中间的是非叶子节点,最下面的是叶子节点。

对于一个普通的二级索引来说,叶子节点存储的是索引key和主键id。

需要注意的是,二级索引在叶子节点中是按照key的顺序从小到大排序的,但是对应的主键id可不一定。

可能与大家想象的不同,MySQL在执行in子查询时,会把in语句中的条件当作一个个的区间,比如:

['b','b'],['f','f'],['g','g']

然后MySQL在二级索引树上,会先查询['b','b']这个区间,比如首先查询到第一个数据页中符合条件的第一条数据(b,2),获取到主键id=2之后,去聚簇索引回表查询所需的数据(因为我们使用的select *,需要获取到所有的列值)。

然后查询第三个数据页中符合条件的第三条数据(f,7),

不断的重复上面的动作。。。。

最后获取到一个结果集,返回到Server,再由Server返回到客户端。

看到这里大家是否可以感觉到,这样查询数据也太麻烦了,特别是当in子查询的条件越来越多时,如何保证性能呢?

下面,我们一起来看一下,MySQL是如何优化in子查询的。

二、物化表

首先,为了演示我们建两张表user和course,并建立两个二级索引idx_user_name和idx_course_name。

CREATE TABLE `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';
CREATE TABLE `course` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  `user_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_userId` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

下面以一个简单的子查询为例:在course表中查询name='中文'的user_id,并作为user表id的查询条件。 

select * from user where id in (select  user_id from course where name='中文')

对于一个这样普通的子查询来说,MySQL使用了一种叫作物化表的方式来提升性能。

什么意思呢?

就是将子查询的结果集去重后放入到一个临时表中,临时表的列就是子查询的结果集中的列。

去重的目的是为了让临时表尽可能的精简,因为在临时表中重复的列并没有什么意义。

当结果集比较小时,MySQL会为临时表使用memory引擎,并且为临时表中的列建立哈希索引。哈希索引的查询时间复杂度是O(1),查询速度是非常快的。

但是如果结果集比较大时,MySQL就会将临时表定义为InnoDB类型表,并且建立B+树索引,就像一个普通的表一样使用。

话说回来,将子查询转换为临时表以后,其实查询就变成了两张表的连接查询,也就是两个表的内连接。

一旦转换为内连接就好办了,经典的“小表驱动大表”的优化准则就可以派上用场了。

我们看一下MySQL优化器对上面的SQL优化后的结果:

select * from user u inner join course c on u.id=c.user_id where c.name='中文'

可以看到,MySQL将其转换为了内连接,并且以user为驱动表,course为被驱动表的方式进行了查询。

由于user表和course表上都有索引,那么此时这个sql的执行速度还是相当可以的。

以下是SQL语句的执行计划

三、SQL优化 

下图是生产环境的慢SQL截图

 执行该SQL语句,耗时3.742923秒

select a.cid, a.aid, a.create_time, a.channel_type, a.user_id, a.event_type, a.ip from testing_put_user_event_type a 
where a.create_time BETWEEN '2023-11-01 00:00:00' AND '2023-11-02 23:59:59' AND a.keep_type = 0 AND
(a.user_id in (164238,423998,500373,548715,589846,616448,699795,920701,988476,993033,1215905,1540914)) and event_type=6

 我们使用子查询代替IN查询,SQL耗时17ms,优化后的效果非常明显。

四、IN语句的优化方式

1. 使用子查询代替IN查询

IN查询在MySQL中是一个非常慢的查询方式,因为它需要对每个值进行比较。可以使用子查询来代替IN查询,子查询可以将结果缓存到内存中,从而提高查询效率。

2. 使用JOIN代替IN查询

使用JOIN代替IN查询也是一种优化方式。JOIN可以将两个或多个表连接起来,使查询更加高效。在使用JOIN时,需要注意查询语句的编写,避免出现笛卡尔积等问题。

3. 使用EXISTS代替IN查询

使用EXISTS代替IN查询也是一种优化方式。EXISTS只需要判断是否存在相关记录,而不需要返回具体的值,因此查询效率更高。

4. 使用索引优化IN查询

在MySQL中,索引是一种非常重要的优化方式。可以使用索引来优化IN查询,在使用索引时,需要注意索引的创建和使用,避免出现索引失效等问题。

5. 优化查询语句

在使用IN查询时,还可以通过优化查询语句来可以使用EXPLAIN命令来查看查询语句的执行计划,找出可能存在的性能瓶颈,从而进行优化。

总结

无论MySQL采用了哪种优化方法,只要知道了其实现的大致原理,对于使用者来说,就有了对应的优化思路。

特别建议大家写完SQL以后,习惯性的使用explain分析一下是否命中了索引,扫描的行数是否过多。

只有不断的实操,优化SQL的能力才会不断提升。

B+ Tree Visualization

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL是一种开源的关系型数据库管理系统,是当前最流行的数据库之一。MySQL优化是为了提高它的性能和响应速度。 MySQL优化的目标是减少数据库操作的时间和资源消耗,提高查询的效率和响应速度。MySQL优化的过程主要包括分析和优化数据库结构、索引优化查询优化、缓存优化等方面。 首先,分析和优化数据库结构是数据库优化的首要任务。应尽量避免重复数据和不规范的数据,优化数据类型、表结构等。此外,应使用合适的数据类型、避免用NULL等避免不必要的数据类型或字段。 其次,索引优化也是MySQL优化的重要方面。正确使用索引可以大大提高查询效率和响应速度,降低服务器资源的消耗。索引优化的原则是尽量创建适当的索引,同时避免过多索引和冗余索引。 查询优化MySQL优化的另一个关键点。可以优化查询语句、调整查询顺序、避免使用长查询语句等方式来提高查询效率。另外,可以使用内存表等技术优化查询,提高服务器响应速度。 最后,缓存优化MySQL优化中的重要环节。MySQL支持多种缓存技术,如查询缓存、MyISAM存储引擎缓存、InnoDB存储引擎缓存等等。合理使用缓存可以缓解高并发请求,降低服务器压力,提高响应速度。 总之,MySQL优化是一个复杂的过程,需要综合分析数据库运行情况和性能瓶颈,针对具体情况采取不同的优化方案。同时需要注意数据库安全和可靠性,保证数据的有效性和一致性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值