1024程序员节|【MySQL从入门到精通】【高级篇】(二十七)外连接和内连接如何进行查询优化呢?join的原理了解一波

news2024/11/20 4:28:31

您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦
💪🏻 1. Python基础专栏,基础知识一网打尽,9.9元买不了吃亏,买不了上当。 Python从入门到精通
❤️ 2.网上优质的Python题库很少,这里给大家推荐一款非常棒的Python题库,从入门到大厂面试题👉点击跳转刷题网站进行注册学习
❤️ 3. Python爬虫专栏,系统性的学习爬虫的知识点。9.9元买不了吃亏,买不了上当 。python爬虫入门进阶
❤️ 4. Ceph实战,从原理到实战应有尽有。 Ceph实战
❤️ 5. Java高并发编程入门,打卡学习Java高并发。 Java高并发编程入门
😁 6. 社区逛一逛,周周有福利,周周有惊喜。码农飞哥社区,飞跃计划

文章目录

    • 1. 简介
    • 2. 数据准备&索引优化说明
      • 2.1. 左连接说明
        • 2.1.1. 没有索引的情况下
        • 2.1.2. 给左表添加索引
        • 2.1.3. 给右表添加索引
      • 2.2. 内连接说明
    • 3. JOIN的原理
      • 3.1. Simple Nested-Loop Join (简单嵌套循环连接)
      • 3.2. Index Nested-Loop Join(索引嵌套循环连接)
      • 3.3. Block Nested-Loop Join(块嵌套循环连接)
    • 4.总结
    • 5. 送书活动
      • 第一类【MySQL数据库进阶实战】:共4本
      • 1. 评论本文获得
      • 2. 参与社区活动获得

1. 简介

上一篇文章我们介绍了
【MySQL从入门到精通】【高级篇】(二十六)建了索引就能用么?我看未必。来看看几种索引失效的情况吧 上篇文章我们将来学习索引失效的几种情况。有时候并不是说加了索引,就一定能用上索引,还是要具体情况具体分析。本文将介绍一下MySQL优化器如何对外连接和内连接进行查询优化的以及介绍Join语句的底层原理。

2. 数据准备&索引优化说明

下面准备了两张数据表,分别是分类表category和图书表book,然后分别在category表和book表中插入17条数据。

-- 分类表
CREATE TABLE IF NOT EXISTS category(
  id INT NOT NULL AUTO_INCREMENT,
	card INT NOT NULL,
	PRIMARY KEY(id)
) ENGINE=INNODB;
-- 图书表
CREATE TABLE IF NOT EXISTS book(
    bookid INT NOT NULL AUTO_INCREMENT,
	card INT NOT NULL,
 	PRIMARY KEY(bookid)
) ENGINE=INNODB;

INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO category(card) VALUES(FLOOR(1+(RAND()*20)));

INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));
INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20)));

在MySQL中的连接分为外连接和内连接。外连接又分为左连接和右连接。
内连接就是取两个表的交集数据。
左连接以左表为准,取左表的全部关联数据
右连接以右表为准,取右表的全部关联数据

2.1. 左连接说明

2.1.1. 没有索引的情况下

EXPLAIN SELECT SQL_NO_CACHE * FROM category LEFT JOIN book ON category.card=book.card;

没有索引的情况下,直接关联两个数据表查看执行计划可以看出两个表的type都是ALL。
在这里插入图片描述

2.1.2. 给左表添加索引

在左连接中,左表作为驱动表,如果给左表添加索引的话,则左表可以使用到索引,这里需要注意的是左表和右表的关联字段的类型要一致呢!

ALTER TABLE category ADD INDEX idx_category_card(card);
EXPLAIN SELECT SQL_NO_CACHE * FROM category LEFT JOIN book ON category.card=book.card;

在这里插入图片描述

2.1.3. 给右表添加索引

给右表添加索引也是同理,添加完索引之后,右表作为被驱动表也是可以使用到该索引的,从而避免全表扫描。

ALTER TABLE book ADD INDEX idx_book_card(card);
EXPLAIN SELECT SQL_NO_CACHE * FROM category LEFT JOIN book
ON category.card=book.card;

在这里插入图片描述
从上图可以看出,在左连接中左表都是作为驱动表,右表作为被驱动表。

2.2. 内连接说明

EXPLAIN SELECT SQL_NO_CACHE * FROM category INNER JOIN book ON category.card=book.card;

在这里插入图片描述
对于内连接来说,查询优化器可以决定谁作为驱动表,谁作为被驱动表出现。虽然两个表都有索引,虽然category表在前面,但是最终查询优化器决定将book表作为驱动表。
下面我们试下删除category表中的索引,接着看下效果如何:

EXPLAIN SELECT SQL_NO_CACHE * FROM category INNER JOIN book ON category.card=book.card;

在这里插入图片描述
我们可以看出对于内连接来讲,如果表的连接条件中只有一个字段有索引,则有索引的字段所在的表会被作为被驱动表出现的。

3. JOIN的原理

join方式连接多个表,本质上就是各个表之间的循环匹配。MySQL5.5 版本之前,MySQL只支持一种表之间关联方式,就是嵌套循环(Nested Loop Join)。如果关联表的数据很大,则join关联的执行时间会非常长,在MySQL5.5 以后的版本中,MySQL通过引入BNLJ算法来优化嵌套执行。

  1. 驱动表和被驱动表
    驱动表就是主表,被驱动表就是从表,非驱动表。

3.1. Simple Nested-Loop Join (简单嵌套循环连接)

算法相当简单,从表A中取出一条数据1,遍历表B,将匹配到的数据放在result,以此类推,驱动表A中的每一条记录与被驱动表B的记录进行判断:
在这里插入图片描述

可以看到这种方式效率是非常低效的,以上述表A数据100条,表B数据1000条计算,则A*B=10万次,开销统计如下:

开销统计SNLJ
外表扫描次数1
内表扫描次数A
读取记录数A+B*A
JOIN比较次数B*A
回表读取记录次数0
当然mysql肯定不会那么粗暴的去进行表的连接,所以就出现了后面的两种对Nested-Loop Join优化算法。

3.2. Index Nested-Loop Join(索引嵌套循环连接)

Index Nested-Loop Join其优化的思路主要是为了减少内层表数据的匹配次数,所以要求被驱动表上必须有索引才行。通过外层表匹配条件直接与内层表索引进行匹配,避免和内层表的每条记录去进行比较,这样极大的减少了对内层表的匹配次数。
在这里插入图片描述

驱动表中的每条记录通过被驱动表的索引进行访问,因为索引查询的成本是比较固定的,故mysql优化器都倾向驱动表中的每条记录通过被驱动表的索引来进行访问,因为索引查询的成本是比较固定的,故mysql优化器都倾向于使用记录数少的表进行驱动表(外表)。

开销统计SNLJINLJ
外表扫描次数11
内表扫描次数A0
读取记录数A+B*AA+B(match)
JOIN比较次数B*AA*index(Height)
回表读取记录次数0B(match)(if possible)
如果被驱动表加索引,效率是非常高的,但如果索引不是主键索引,还得进行一次回表查询,所以,如果被驱动表的索引是主键索引,效率会更高。

3.3. Block Nested-Loop Join(块嵌套循环连接)

如果存在索引,那么会使用index的方式进行join,如果join的列没有索引,被驱动表扫描的次数太多了,每次访问被驱动表,其表中的记录都会被加载到内存中,然后再从驱动表中取一条与其匹配,匹配结束后清除内存,然后再从驱动表中加载一条记录,然后把被驱动表的记录在加载的记录再加载到内存匹配,这样周而复始,大大增加了IO的次数。为了减少被驱动表的IO次数,就出现了Block Nested-Loop Join的方式。
在这里插入图片描述

不再是逐条获取驱动表的数据,而是一块一块的获取,引入join buffer缓冲区,将驱动表join相关的部分数据列(大小受join buffer的限制)缓存到join buffer中,然后全表扫描被驱动表,被驱动表的每一条记录一次性和join buffer中的所有驱动表记录进行匹配(内存中操作),将简单嵌套循环中多次比较合并成一次,降低了被驱动表的访问频率。

注意:
这里缓存的不只是关联表的列,select后面的列也会缓存起来。
在一个有N个join关联的sql中分配N-1个join buffer,所以查询的时候尽量减少不必要的字段,可以让 join buffer中可以存放更多的列。

开销统计SNLJINLJBNLJ
外表扫描次数111
内表扫描次数A0A*used_column_size/join_buffer_size+1
读取记录数A+B*AA+B(match)A+B(A*used_column_size/join_buffer_size)
JOIN比较次数B*AA*index(Height)B*A
回表读取记录次数0B(match)(if possible)0

参数设置:

  • block_nested_loop
    通过show variables like '%optimizer_switch%' 查看block_neseted_loop 状态。默认是开启的。
  • join_buffer_size
    驱动表能不能一次性加载完,要看join buffer 能不能存储所有的数据,默认情况下 join_buffer_size=256k
    在这里插入图片描述
    join_buffer_size的最大值在32位系统可以申请4G,而在64位操作系统中可以申请大于4G的Join Buffer空间(64位)

4.总结

  1. 整体效率比较:INLJ>BNLJ>SNLJ
  2. 永远用小结果集驱动大结果集(其本质是减少外层循环的数据数量)(这里的小结果集并不一定指数据量少的表,而是值通过筛选条件得到的 表行数*每行大小)
SELECT SQL_NO_CACHE * FROM category INNER JOIN book ON category.card=book.card WHERE book.bookid<100;   //推荐
SELECT SQL_NO_CACHE * FROM category INNER JOIN book ON category.card=book.card WHERE category.id<100;   //不推荐
  1. 为被驱动表匹配的条件增加索引(减少内层表的循环匹配次数)
  2. 增大join buffer size的大小(一次缓存的数据越多,那么内层包的扫描次数就越少)
  3. 减少驱动表不必要的字段查询(字段越少,join buffer所缓存的数据就越多)

5. 送书活动

为了回馈广大粉丝们长期以来的厚爱,本博主决定给小伙伴们送出2类共4本图书。在此特别感谢 机械工业出版社有限公司的赞助,所有图书均包邮包邮包邮!!!!

第一类【MySQL数据库进阶实战】:共4本

《MySQL数据库进阶实战》,本书是作者基于多年的教学与实践进行的总结,重点介绍了MySQL数据库的核心原理与体系架构,涉及开发、运维、管理与架构等知识。全书共12章,包括MySQL数据库基础、详解 InnoDB存储引擎、MySQL用户管理与访问控制、管理MySQL的数据库对象、MySQL应用程序开发、MySQL的事务与锁、MySQL备份与恢复、MySQL的主从复制与主主复制、MySQL的高可用架构、MySQL性能优化与运维管理、MySQL数据库的监控和使用MySQL数据库的中间件。读者根据本书中的实战步骤进行操作,可以在实际项目的生产环境中快速应用并实施MySQL。
在这里插入图片描述

1. 评论本文获得

  1. 本文优质评论两条,且该评论点赞数是最高的,分别获得《MySQL数据库进阶实战》一本!点赞数一样的以提交时间为准,提前时间靠前的优先。

2. 参与社区活动获得

  1. ​参与社区活动,精选优质提交者两人,每人赠送一本《MySQL数据库进阶实战》!
    参与的方式是:
  2. 在1024活动帖评论区,回复:“我要报名” 👉 点击跳转到活动贴参与活动吧
  3. 提交在 👉 “1024 程序员节|用代码,改变世界” 主题征文活动 👈 投稿后,同步提交链接至本社区活动帖的任务区。
    统计截止时间:2022/10/30 20:00:00

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/450.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[面试宝典] Linux常见命令及面试题

前言&#xff1a; &#x1f604;作者简介&#xff1a;小曾同学.com,小伙伴们也可以叫我小曾&#xff0c;一个致力于测试开发的博主⛽️ 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。&#x1f60a; 座右铭&#xff1a;…

C语言 - 你一定能看懂的扫雷万字详解(加入了递归展开和手动标雷的功能)

C语言之扫雷详解&#xff08;包含递归展开和手动标雷功能&#xff0c;非常强大&#xff01;&#xff09; 文章目录一.写在前面二.效果展示三.整体逻辑四.详解1.进入主函数&#xff0c;打印菜单&#xff0c;玩家作出选择2.定义棋盘的数组并进行赋值3.棋盘的展示4.随机布雷5.开始…

前端开发入门--html--Flask

快速开发网站 pip install flaskfrom flask import Flaskapp Flask(__name__)# 创建了网址 /show/info 和 函数index 的对应关系 # 以后用户在浏览器上访问 /show/info&#xff0c;网站自动执行 index app.route("/show/info") def index():return "中国联通&…

TPH-YOLOv5 | 基于Transformer的YOLOv5小目标检测器 | 四头加注意力

论文地址&#xff1a;https://arxiv.org/pdf/2108.11539.pdf 项目地址&#xff1a;https://github.com/cv516Buaa/tph-yolov5 在无人机捕获的场景中进行对象检测是最近的一项热门任务。由于无人机总是在不同的高度航行&#xff0c;物体尺度变化剧烈&#xff0c;给网络优化带来…

NMEA协议解析

文章目录一、NMEA0183协议1、NMEA基本框架2、常用语句1&#xff09;GNGGA2&#xff09;GNGLL3&#xff09;GNGSA4&#xff09;GPGSV5&#xff09;GNRMC6&#xff09;GNVTG7&#xff09;GNZDA8&#xff09;PAIRCLK等二、异或校验和代码1、网址在线计算BCC2、BCC校验和代码一、NM…

Java语言中的异常处理

异常处理 在java语言中&#xff0c;很机智的将异常作为对象来处理&#xff0c;而且定义一个基类java.lang.Throwable作为所有异常类的父类。在这许多类中一般分为两大类&#xff1a; 错误类(Error)和异常类&#xff08;Expception&#xff09;。 如图&#xff1a; 注&#xf…

iNOF在现实网络中的运用,以带反射器的iONF为例

定义 iNOF&#xff08;Intelligent Lossless NVMe Over Fabric&#xff0c;智能无损存储网络&#xff09;是指通过对接入主机的快速管控&#xff0c;将智能无损网络应用到存储系统&#xff0c;实现计算和存储网络融合的技术。 目的 网络转发设备用于传输流量&#xff0c;不同类…

竞争不是内卷,用头脑学习,而非时间

文章目录 用头脑学习&#xff0c;而非时间 前言 一、自由竞争不是内卷 二、内卷都在哪些行业 三、高效学习来大数据梦想联盟 用头脑学习&#xff0c;而非时间 前言 大多数人不懂&#xff0c;不会&#xff0c;不做&#xff0c;才是你的机会&#xff0c;你得行动&#xff…

【Queue】- 从源码分析ArrayDeque及其常用方法

文章目录概述ArrayDeque基础知识ArrayDeque内部结构ArrayDeque的构造方法ArrayDeque的扩容操作ArrayDeque常用方法将ArrayDeque作为双端队列使用时public void addFirst(E e)public void addLast(E e)public boolean offerFirst(E e)public boolean offerLast(E e)public E pol…

动态SLAM论文归纳

持续更新&#xff0c;持续更新 2022 Multi-modal Semantic SLAM for Complex Dynamic Environments 作者&#xff1a;Han Wang, Jing Ying Ko and Lihua Xie, Fellowcode&#xff1a;https://github.com/wh200720041/MMS_SLAM视频&#xff1a;https://www.youtube.com/watch…

web自动化测试——入门篇01

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

并发编程中的原子性,可见性,有序性问题

前言&#xff1a;大家好&#xff0c;我是小威&#xff0c;24届毕业生&#xff0c;在一家满意的公司实习。本篇文章是关于并发编程中出现的原子性&#xff0c;可见性&#xff0c;有序性问题。 本篇文章记录的基础知识&#xff0c;适合在学Java的小白&#xff0c;也适合复习中&am…

PyTorch(三)TensorBoard 与 Transforms

文章目录Log一、TensorBoard1. TensorBoard 的安装2. SummaryWriter 的使用① add_scalar() 的使用a. 参数说明b. 函数使用c. 使用 Tensorboard② add_image() 的使用a. 参数说明b. 使用 numpy.array() 对 PIL 图片进行转换c. 使用函数d. 改变 global_step二、Transforms1. Tra…

数据结构 | 时间复杂度与空间复杂度

… &#x1f333;&#x1f332;&#x1f331;本文已收录至&#xff1a;数据结构 | C语言 更多知识尽在此专栏中&#xff01; &#x1f389;&#x1f389;&#x1f389;欢迎点赞、收藏、关注 &#x1f389;&#x1f389;&#x1f389;文章目录&#x1f333;前言&#x1f333;正…

【C++初阶】类和对象(二)

大家好我是沐曦希&#x1f495; 类和对象1.类的6个默认成员函数2.构造函数2.1 概念2.2 特性3.析构函数3.1 概念3.2 特性4.拷贝构造函数4.1 概念4.2 特征1.类的6个默认成员函数 空类&#xff1a;类中一个成员都没有 可是空类真的什么都没有吗&#xff1f; 并不是&#xff0c;任…

STM32关于UART的接收方式

STM32的 UART 一般分为定长接收和不定长接收 定长接收&#xff1a; HAL_UART_Receive():只能接收固定长度的数据&#xff0c;如果超过固定长度的数据只能接收对应长度&#xff0c;如果小于固定长度则不会接收 HAL_UART_Receive_IT():中断方式接收&#xff0c;每接收一个字节…

CSS 2 CSS 选择器 - 5 2.8 伪选择器 2.8.1 伪类选择器【根据特定状态选取元素】

CSS 文章目录CSS2 CSS 选择器 - 52.8 伪选择器2.8.1 伪类选择器【根据特定状态选取元素】2 CSS 选择器 - 5 2.8 伪选择器 2.8.1 伪类选择器【根据特定状态选取元素】 【什么是伪类】 伪类用于定义元素的特殊状态。 例如&#xff0c;它可以用于&#xff1a; 设置鼠标悬停在…

如何删除ZIP压缩包的密码?

ZIP是比较常用的压缩文件格式&#xff0c;有时候因为工作需要很多人还会给压缩包设置打开密码。那如果后续不需要密码保护了要如何删除密码呢&#xff1f;密码忘记了还能删除吗&#xff1f; 首先来说说第一种情况&#xff0c;也就是知道密码但后续不需要密码保护&#xff0c;只…

1. 初识Python

1. Pythond 简介 Python 语言由荷兰的 Guido Van Rossum (吉多范罗苏姆, 江湖人称龟叔) 在1989年圣诞节期间为了打发圣诞节的无趣而开发的一个脚本解释语言.Python 源代码遵循 GPL(GNU General Public License)开源协议, 也就是说你可以免费使用和传播它, 而不用担心版权的问…

libusb系列-005-部分API简介

libusb系列-005-部分API简介 文章目录libusb系列-005-部分API简介摘要libusb_initlibusb_open_device_with_vid_pidlibusb_kernel_driver_activelibusb_detach_kernel_driverlibusb_claim_interfacelibusb_release_interfacelibusb_attach_kernel_driverlibusb_closelibusb_exi…