百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

写出好的Join语句,前提你得懂这些

wptr33 2025-02-26 14:04 17 浏览

本篇文章收录在:http://upheart.top/

前言

最近在读《MySQL性能调优与架构设计》,看到一个关于join的优化原则,如下:

大白话解释下:

因为驱动结果集越大,意味着需要循环的次数越多,也就是说在被驱动结果集上面所 需要执行的查询检索次数会越多。

比如,当两个表(表 A 和 表 B) Join 的时候,如果表 A 通过 WHERE 条件过滤后有 10 条记录,而表 B 有 20 条记录。如果我们选择表 A 作为驱动表,也就是被驱动表的结果集为 20,那么我们通过 Join 条件对被驱动表(表 B)的比较过滤就会有 10 次。反之,如果我们选择表 B 作为驱动表,则需要有 20 次对表 A 的比较过滤。

小贴士1:驱动表的定义:当进行多表连接查询时,1.指定了联接条件时,满足查询条件的记录行数少的表为驱动表,2.未指定联接条件时,行数少的表为驱动表

小贴士2:关联查询的概念:MySQL 表关联的算法是 Nest Loop Join,是通过驱动表的结果集作为循环基础数据,然后一条一条地通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果

所以本文就从这个地方开始,学习下mysql join的相关知识

基本介绍

left join、right join、inner join的区别

相信大家都知道这个,简单介绍下

  • left join(左连接):返回包括左表中的所有记录和右表中联结字段相等的记录
  • right join(右连接):返回包括右表中的所有记录和左表中联结字段相等的记录
  • inner join(等值连接):只返回两个表中联结字段相等的行

一张大图, 清楚明了:

那我们看看在join连接时哪个表是驱动表,哪个表是被驱动表:

  • 1.当使用left join时,左表是驱动表,右表是被驱动表
  • 2.当使用right join时,右表是驱动表,左表是被驱动表
  • 3.当使用inner join时,mysql会选择数据量比较小的表作为驱动表,大表作为被驱动表

注意:当连接查询有where条件时,带where条件的表是驱动表,否则是被驱动表

具体情况大家可以用Explain执行计划验证下

Explain使用可以参考我之前的文章:最完整的Explain总结,SQL优化不再困难

举个例子:

假如有两张表:A是小表,B是大表

使用left join 时,则应该这样写

select?*?from?A?a?left?join?B?b?on?a.id=b.id;

此时A表驱动表,B表是被驱动表

测试:假设A表140多条数据,B表20万左右的数据量

select?*?from?A?a?left?join?B?b?on?a.id=b.id;

执行时间:8s

select?*?from?B?b?left?join?A?a?on?a.id=b.id;

执行时间:19s

所以记住:小表驱动大表优于大表驱动小表

一个注意点

join查询在有索引条件下

  • 驱动表有索引不会使用到索引
  • 被驱动表建立索引会使用到索引

所以在以小表驱动大表的情况下,再给大表建立索引会大大提高执行速度

举例子测试一下:

假设有2张表:A表,B表,分别建立索引

select?*?from?A?a?left?join?B?b?on?a.name=b.name;

发现只有B表name使用到索引

如果同时只给A表的name建立索引会是什么情况?

在这种情况下,A表索引失效

所以可以通过给被驱动表建立索引来优化SQL

Join原理

mysql的join算法叫做Nested-Loop Join(嵌套循环连接)

而这个Nested-Loop Join有三种变种,下面分别介绍下

Simple Nested-Loop

这个算法相当简单、直接。即驱动表中的每一条记录与被驱动表中的记录进行比较判断(就是个笛卡尔积)。对于两表联接来说,驱动表只会被访问一遍,但被驱动表却要被访问到好多遍

假设R为驱动表,S被驱动表,用伪代码表示一下这个过程就是这样:

for?r?in?R??????????????????????--?扫描R表(驱动表)
????for?s?in?S????????????????????--?扫描S表(被驱动表)
????????if?(r?and?s?satisfy?the?join?condition)??--?如果r和s满足join条件
????????????output?result????--?返回结果集

所以如果R有1万条数据,S有1万条数据,那么数据比较的次数1万 * 1万 =1亿次,这种查询效率会非常慢。

Index Nested-Loop

这个是基于索引进行连接的算法

它要求被驱动表上有索引,可以通过索引来加速查询。

假设R为驱动表,S被驱动表,用伪代码表示一下这个过程就是这样:

For?r?in?R??????????????????--?扫描R表
????for?s?in?Sindex????????????????????--?查询S表的索引(固定3~4次IO,B+树高度)
????????if?(s?==?r)???????????????????--?如果r匹配了索引s
????????????output?result???--?返回结果集

Block Nested-Loop

这个算法较Simple Nested-Loop Join的改进就在于可以减少被驱动表的扫描次数

因为它使用Join Buffer来减少内部循环读取表的次数

假设R为驱动表,S被驱动表,用伪代码表示一下这个过程就是这样:

for?r?in?R?????????????????????????????--?扫描表R
????store?p?from?R?in?Join?Buffer????--?将部分或者全部R的记录保存到Join?Buffer中,记为p
????for?s?in?S????????????????????????--?扫描表S
????????if?(p?and?s?satisfy?the?join?condition)????????--?p与s满足join条件
???????????output?result????????????????????--?返回为结果集

可以看到相比Simple Nested-Loop Join算法,Block Nested-LoopJoin算法仅多了一个所谓的Join Buffer

为什么这样就能减少被驱动表的扫描次数呢?

下图相比更好地解释了Block Nested-Loop Join算法的运行过程

可以看到Join Buffer用以缓存联接需要的列(所以再次提醒我们,最好不要把*作为查询列表,只需要把我们关心的列放到查询列表就好了,这样还可以在join buffer中放置更多的记录呢,是不是这个道理哈,哈哈)

然后以Join Buffer批量的形式和被驱动表中的数据进行联接比较。

关于Join Buffer

  1. Join Buffer会缓存所有参与查询的列而不是只有Join的列。
  2. join_buffer_size的默认值是256K

总结

在选择Join算法时,会有优先级:

Index Nested-LoopJoin > Block Nested-Loop Join > Simple Nested-Loop Join

当不使用Index Nested-Loop Join的时候,默认使用Block Nested-Loop Join

使用Block Nested-Loop Join算法需要开启优化器管理配置的optimizer_switch的设置block_nested_loop为on,默认为开启。

Join优化

通过上面的简单介绍,可以总结出以下几种优化思路

1.用小结果集驱动大结果集,减少外层循环的数据量

2.如果小结果集和大结果集连接的列都是索引列,mysql在join时也会选择用小结果集驱动大结果集,因为索引查询的成本是比较固定的,这时候外层的循环越少,join的速度便越快。

3.为匹配的条件增加索引:争取使用Index Nested-Loop Join,减少内层表的循环次数

4.增大join buffer size的大小:当使用Block Nested-Loop Join时,一次缓存的数据越多,那么外层表循环的次数就越少,减少不必要的字段查询:

5.当用到Block Nested-Loop Join时,字段越少,join buffer 所缓存的数据就越多,外层表的循环次数就越少;

觉得有收获,帮忙点赞,转发,分享下吧,谢谢

参考:

官网:https://dev.mysql.com/doc/refman/8.0/en/

书籍:MySQL性能调优与架构设计

相关推荐

MySQL进阶五之自动读写分离mysql-proxy

自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...

Postgres vs MySQL_vs2022连接mysql数据库

...

3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?

引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...

一文由浅入深带你完全掌握MySQL的锁机制原理与应用

本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...

验证Mysql中联合索引的最左匹配原则

后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...

MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)

目录1.索引基础...

你会看 MySQL 的执行计划(EXPLAIN)吗?

SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...

MySQL 从入门到精通(四)之索引结构

索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...

mysql总结——面试中最常问到的知识点

mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...

mysql总结——面试中最常问到的知识点(2)

首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...

MySQL基础全知全解!超详细无废话!轻松上手~

本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...

深入剖析 MySQL 中的锁机制原理_mysql 锁详解

在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...

Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析

引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...

MySQL基础篇:DQL数据查询操作_mysql 查

一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...

MySql:索引的基本使用_mysql索引的使用和原理

一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...