mysql Join查询与优化思路详解

MySQL Query 的优化—Join的实现原理及优化思路: https://blog.csdn.net/qq_39408664/article/details/119205778


Nested-Loop Join 算法解释

官网Join算法: https://www.docs4dev.com/docs/zh/mysql/5.7/reference/nested-loop-joins.html

Simple Nested-Loop Join

如下图,r为驱动表,s为匹配表,可以看到从 r 中分别取出 r1、r2、…、rn 去匹配 s 表的左右列,然后再合并数据,对 s 表进行了 rn 次访问,对数据库开销大。


Index Nested-Loop Join(索引嵌套)

这个要求非驱动表(匹配表s)上有索引,可以通过索引来减少比较,加速查询。在查询时,驱动表( r )会根据关联字段的索引进行查找,当在索引上找到符合的值,再回表进行查询,也就是只有当匹配到索引以后才会进行回表查询。如果非驱动表(s)的关联键是主键的话,性能会非常高,如果不是主键,要进行多次回表查询,先关联索引,然后根据二级索引的主键ID进行回表操作,性能上比索引是主键要慢。
请添加图片描述

Block Nested-Loop Join

如果有索引,会选取第二种方式进行 Join ,但如果 Join 列没有索引,就会采用 Block Nested-Loop Join。可以看到中间有个 Join Buffer 缓冲区,是将驱动表的所有 Join 相关的列都先缓存到 Join Buffer 中,然后批量与匹配表进行匹配,将第一种多次比较合并为一次,降低了非驱动表(s)的访问频率。默认情况下 join_buffer_size=256K ,在查找的时候 MySQL 会将所有的需要的列缓存到 Join Buffer 当中,包括 select 的列,而不是仅仅缓存关联列。在一个有 N 个 Join 关联的SQL当中会在执行时候分配 N-1 个 Join Buffer。
请添加图片描述
join_buffer_size 官方解释:https://www.docs4dev.com/docs/zh/mysql/5.7/reference/server-systemvariables.html

用于普通索引扫描,范围索引扫描和不使用索引的连接的缓冲区的最小大小,从而执行全表扫描。通常,获得快速连接的最佳方法是添加索引。当无法添加索引时,增加 join_buffer_size 的值以获得更快的完全连接。为两个表之间的每个完整连接分配一个连接缓冲区。对于未使用索引的多个表之间的复杂连接,可能需要多个连接缓冲区。




Join 实例的优化

女性客户的数量与平均月薪 & 不同城市的客户数量与平均月薪

可以先通过 explain 进行分析:
在这里插入图片描述在这里插入图片描述
通过如上分析,第一个SQL使用的是 customers 作为驱动表,而第二个则是使用的是 salary 作为驱动表。之所以选择主要是因为MySQL的优化器会先选取两个表的结构信息,根据表的大小优先选择小表然后再选择大表,但是在其中有条件的情况下会选择与根据条件筛选之后的数据表,相对来说数据会少一些,当然优化就比较简单,分别对于 salary 与 customers 表建立对应的索引就可以(gender,city)和(monthsalary);

建立索引:

建立一个性别与地区的索引
alter table customers add index idx_gender_city(gender,city);
在薪资表中建立月薪相关索引
alter table salary add index idx_monthsalary(monthsalary);

查看查询效率

select count(*) ,avg(s.monthsalary) from customers c,salary s where c.gender = 0 and c.id = s.id;
在这里插入图片描述
select count(*),avg(s.monthsalary) from customers c,salary s where c.id = s.id group by c.city;
在这里插入图片描述

发现第二句的语句在加了索引后反而效率更低了,我们可以稍微调整下SQL语句:

select count(*),avg(salary.monthsalary) from customers left join salary on customers.id = salary.id group by customers.city;
在这里插入图片描述


列出没有手机号码,或者没有照片,或者没有年终奖的客户姓名

首先我们使用 explain 分析一下语句:

explain select name from customers,salary where customers.id=salary.id and (mobile = '0' or photo = '0' or yearbonus = 0);
在这里插入图片描述
在这里插入图片描述

现在改为之前的 union all 在前面的方法对于一个表的优化方式:

select name from customers,salary where customers.photo = '0' and customers.id = salary.id
union all
select name from customers,salary where customers.mobile = '0' and customers.id = salary.id
union all
select name from customers,salary where salary.yearbonus = 0 and customers.id = salary.id

在这里插入图片描述
执行的效率不理想,可以适当调整SQL语句,如下:

select name from customers where photo = '0' and mobile = '0'
union all
select name from customers where id in (select id from salary where salary.yearbonus = 0);

那么这个时候我们就可以单独针对于如上的两条SQL进行优化实际上最好的优化方式是(建立索引)

alter table customers add index idx_mobile_photo_name(mobile,photo,name);
alter table salary add index idx_yearbonus(yearbonus);

对于 idx_mobile_photo_name 的解释,在 where 中我们对于 mobile , photo , name 定义了一个关联的索引对于MySQL来说:会查找 where 上的(所有)字段是否包含在了某一个索引中,如果存在就会生效。




Join 查询优化思路

  1. 尽可能减少 Join 语句中的 Nested Loop 的循环总次数;如何减少 Nested Loop 的循环总数?最有效的办法只有一个,那就是让驱动表的结果集尽可能的小。为什么?因为驱动结果集越大,意味着需要循环的次数越多,也就是说在被驱动结果集上面所需要执行的查询检索次数会越多。比如,当两个表( 表A和表B )Join 的时候,如果表A 通过 where 条件过滤后有 10 条记录,而表B 有 20 条记录。如果我们选择表A 作为驱动表,也就是被驱动表(表B)的结果集为 20 ,那么我们通过 Join 条件对被驱动表(表B)的比较过滤就会有 10 次。反之,如果我们选择被驱动表(表B)作为驱动表,则需要有 20 次对表A 的比较过滤。当然,此优化的前提条件是通过 Join 条件对各个表的每次访问的资源消耗差别不是太大。如果访问存在较大的差别的时候(一般都是因为索引的区别),我们就不能简单的通过结果集的大小来判断需要 Join 语句的驱动顺序,而是要通过比较循环次数和每次循环所需要的消耗的乘积的大小来得到如何驱动更优化。

  2. 优先优化 Nested Loop 的内层循环;不仅仅是在数据库的 Join 中应该做的,实际上在我们优化程序语言的时候也有类似的优化原则。内层循环是循环中执行次数最多的,每次循环节约很小的资源,在整个循环中就能节约很大的资源。

  3. 保证 Join 语句中被驱动表上 Join 条件字段已经被索引;保证被驱动表上 Join 条件字段已经被索引的目的,正是针对上面两点的考虑,只有让被驱动表的 Join 条件字段被索引了,才能保证循环中每次查询都能够消耗较少的资源,这也正是优化内层循环的实际优化方法。

  4. 当无法保证被驱动表的 Join 条件字段被索引且内存资源充足的前提下,不要太吝啬 Join Buffer 的设置:当在某些特殊的环境中,我们的 Join 必须是 All,index,range 或者是 index_merge 类型的时候, Join Buffer 就会派上用场了。在这种情况下, Join Buffer 的大小将对整个 Join 语句的消耗起到非常关键的作用。




  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL的EXPLAIN语句用于分析和优化查询语句的执行计划。通过执行EXPLAIN语句,你可以了解MySQL是如何处理你的查询,包括表的访问顺序、使用的索引、连接方式等。 下面是使用EXPLAIN语句来优化SQL查询的步骤: 1. 确定要优化查询语句。可以使用SELECT语句来查询数据,然后在该语句前加上EXPLAIN关键字。例如:EXPLAIN SELECT * FROM table_name WHERE condition; 2. 执行EXPLAIN语句,并查看结果。执行EXPLAIN后,MySQL会返回一张表格,其中包含了查询的执行计划信息。这些信息可以帮助你分析查询的性能瓶颈。 3. 分析执行计划信息。在执行计划信息表格中,你可以关注以下几个重要的列: - id: 查询的唯一标识符。多表查询时,每个表都有一个唯一标识符。 - select_type: 查询类型。包括简单查询、联接查询、子查询等。 - table: 查询涉及的表名。 - type: 表示MySQL访问表的方式,常见的有ALL、index、range、ref等。 - possible_keys: 表示可能使用的索引。 - key: 实际使用的索引。 - rows: 估计扫描的行数。 - Extra: 额外的信息,如是否使用了临时表、是否使用了文件排序等。 4. 根据执行计划信息来进行优化。根据查询的复杂性和性能要求,你可以采取以下一些优化措施: - 确保表中的列上有适当的索引。 - 使用JOIN语句时,确保连接条件上有索引。 - 避免在查询中使用通配符(如SELECT *)。 - 尽量减少子查询的使用。 - 优化WHERE条件,尽量避免使用OR、NOT等复杂的逻辑判断。 - 使用合适的数据类型,避免不必要的数据类型转换。 通过不断地执行EXPLAIN语句,分析执行计划信息,并针对性地进行优化,你可以提高查询的性能并减少数据库的负载。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值