MySQL的join你真的了解吗!!!

news2025/1/21 13:03:25

1.测试用例数据库信息

本文章采用的数据库结构,以及MySQL版本:5.7

t1 表,有一个主键id,字段a,字段b。 (此表建立了一个索引a) 数据大约1000条
在这里插入图片描述
t2 表,有一个主键id,字段a,字段b。 (此表也建立了一个索引a) 数据大约100w条
在这里插入图片描述


2.join的常见用法

1. inner jon 内连接
内连接又叫等值连接,此时的inner可以省略。获取两个表中有匹配关系的记录,即两表取交集

select  *
from t1 as t
join t2 as tt  on t.id=tt.id

2. left join 左连接
以左表为基础,获取匹配关系的记录,如果右表中没有匹配项,NULL表示

select  *
from t1 as t
left join t2 as tt  on t.id=tt.id

3. right join 右连接
以右表为基础,获取匹配关系的记录,如果左表中没有匹配项,NULL表示

select  *
from t1 as t
right join t2 as tt  on t.id=tt.id

4. straight_join
straight_join 让 MySQL 使用固定的连接方式执行查询,这样优化器只会按照我们指定的方式去 join。其中,t1 是驱动表,t2 是被驱动表。

select  *
from t1 as t
straight_join t2 as tt  on t.id=tt.id

3.join语句的执行流程

执行如下SQL语句,此时t1 是驱动表,t2 是被驱动表。

select * from t1 straight_join t2 on (t1.a=t2.a);

使用explain查看执行详情
在这里插入图片描述
图中你可以看到驱动表t1进行了全表扫描,t2表使用了索引a。

在这里插入图片描述
这个语句执行流程如下

1.从表 t1 中读入一行数据 R;

2.从数据行 R 中,取出 a 字段到表 t2 里去查找;

3.取出表 t2 中满足条件的行,跟 R 组成一行,作为结果集的一部分;

4.重复执行步骤 1 到 3,直到表 t1 的末尾循环结束。

那么此流程一共扫描了多少行?

1. t1表是全表扫描因此扫描了1000行。

2. 因为t2表走的是树搜索过程。由于我们构造的数据都是一一对应的,因此每次的搜索过程都只扫描一行,也是总共扫描 1000 行;

3. 整个执行流程,总扫描行数是 2000行。

那么,如果t2表没有使用索引它的流程是怎样呢?


4. Simple Nested-Loop Join 和Block Nested-Loop Join

1.Simple Nested-Loop Join (在5.5版本之前会使用)

当我们执行如下SQL时,t1表还是进行全表扫描,但是t2表上b字段没有索引

select * from t1 straight_join t2 on (t1.a=t2.b);

他的执行流程和上面还是一样,还是从t1表一行行取出来去t2表中找,那么他扫描了多少行?

1. t1表是全表扫描因此扫描了1000行。

2. 因为t2表走的是全表扫描,也就是扫描 100w 行;

3. 整个执行流程,总扫描行数是 1000 * 100w = 10亿行。

这个很笨重的算法叫做Simple Nested-Loop Join,不过MySQL并没有使用这个算法,而是使用了Block Nested-Loop Join算法。

2.Block Nested-Loop Join (5.5版本以后)

当我们执行如下SQL时,t1表还是进行全表扫描,但是t2表上b字段没有索引

select * from t1 straight_join t2 on (t1.a=t2.b);

在这里插入图片描述

他的执行流程和上面还是一样,还是从t1表一行行取出来去t2表中找,那么他扫描了多少行?

1.把表 t1 的数据读入线程内存 join_buffer 中,由于我们这个语句中写的是 select *,因此是把整个表 t1 放入了内存;

2.扫描表 t2,把表 t2 中的每一行取出来,跟 join_buffer 中的数据做对比,满足 join 条件的,作为结果集的一部分返回。

由于 join_buffer 是以无序数组的方式组织的,因此对表 t2 中的每一行,都要做 100w 次判断,总共需要在内存中做的判断次数是:1000*100w=10 亿次 。因此,从时间复杂度上来说,这两个算法是一样的。但是,Block Nested-Loop Join 算法的这 10 亿次判断是内存操作,速度上会快很多,性能也更好。


5.使用Block Nested-Loop Join的注意事项

1.可能会多次扫描被驱动表,占用磁盘 IO 资源;
当我们在使用Block Nested-Loop Join 算法时,我们驱动表会放入join_buffer中,但是会把被驱动表的数据一行一行的取出来跟 join_buffer 中的数据做对比。如果被驱动表有1亿条数据,被驱动表就要扫描1亿次,非常占用io资源。

2.可能会导致 Buffer Pool 的热数据被淘汰,影响内存命中率。

join_buffer 是由 join_buffer_size参数控制的,所以 join_buffer 也有一定大小。如果我们join_buffer放慢了怎么办?

现在假如一个场景? join_buffer每次只能放入t1表60%的数据。

1.扫描表 t1,顺序读取数据行放入 join_buffer 中,在放入60%数据后join_buffer 满了,
2. 扫描表 t2,把 t2 中的每一行取出来,跟 join_buffer 中的数据做对比,满足 join 条件的,作为结果集的一部分返回;
3. 清空 join_buffer;继续扫描表 t1,顺序读取最后的 12 行数据放入 join_buffer 中
4. 扫描表 t2,把 t2 中的每一行取出来,跟 join_buffer 中的数据做对比,满足 join 条件的,作为结果集的一部分返回;

在上述例子中,我们发现t1表扫描了两次。t2表也扫描了两次。根据我们MySQL的LRU算法

处于 old 区域的数据页,每次被访问的时候都要做下面这个判断:

1.若这个数据页在 LRU 链表中存在的时间超过了 1 秒,就把它移动到链表头部;

2.如果这个数据页在 LRU 链表中存在的时间短于 1 秒,位置保持不变。1 秒这个时间,是由参数 innodb_old_blocks_time 控制的。其默认值是 1000,单位毫秒。

由于优化机制的存在,一个正常访问的数据页,要进入 young 区域,需要隔 1 秒后再次被访问到。在上述例子中,如果我们join_buffer更小,被驱动表就会被多次扫描,而且这个语句执行时间超过 1 秒,就会在再次扫描t2表的时候,把t2表的数据页移到 LRU 链表头部。这时我们Buffer Pool 的热数据就会被淘汰,影响内存命中率。


6.join语句到底需要这么优化

执行SQL语句

EXPLAIN select * from t1 join t2 on (t1.b=t2.b) where t2.b>=1 and t2.b<=400000;

t1和t2表都进行了全表扫描,t1表扫描1000行,t2表扫描了970748行
在这里插入图片描述
执行这个SQL语句,我们耗时了46.857s
在这里插入图片描述
执行这条SQL语句时,由于b字段上没有建立索引,导致t2表需要进行全表扫描。如果建立索引又非常消耗系统资源,不建立索引的情况下。怎么进行优化
这时候,我们可以考虑使用临时表。使用临时表的大致思路是:

1.把表 t2 中满足条件的数据放在临时表 tmp_t 中;

2.给临时表 tmp_t 的字段 b 加上索引;

3.让表 t1 和 tmp_t 做 join 操作。

create temporary table temp_t(id int primary key, a int, b int, index(b))engine=innodb;  创建临时表, 建立索引
insert into temp_t select * from t2 where b>=1 and b<=2000; 插入数据
select * from t1 join temp_t on (t1.b=temp_t.b); 

注意事项:当我们使用范围查询时。 Mysql发现通过索引扫描的行记录数超过全表的25% 时,优化器可能会放弃走索引,自动变成全表扫描。

在这里插入图片描述
执行这个SQL语句,我们耗时了0.318s。


7. MySQL的 hash join

1.如果我们把驱动表1000条数据存人map,key是字段对应的值。value就是这行数据的值。
2.在把100w条数据读出来,循环遍历这100w条数据。
3.在从100w条数据的每一行拿出对应key字段的值,如果key拿出来有值,就把这行数据结果存入map并返回。

这样我们10亿次比对就会变成100次hash查找

所以在MySQL8.0之后版本,MySQL就加入了hash join。如下是我使用MySQL8.0版本进行查询,耗时0.610s。
在这里插入图片描述


8.join总结

1. 应该使用小表作为驱动表

2. 被驱动表关键字段应该建立索引

3.被驱动表无法使用索引、数据量很大时,因适当调整join_buffer大小。避免 Buffer Pool 的热数据被淘汰,影响内存命中率。

4.MySQL8.0之后版本,已经支持hash join,没有升级的小伙伴赶紧升级把!!!


在这里插入图片描述
阿根廷加油,梅老板牛逼!!!


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

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

相关文章

C++标准库分析总结(十一)——<适配器>

目录 1 适配器简介 2 适配器使用分类 2.1 容器适配器 2.2 函数适配器 2.2.1 常见的函数适配器 2.2.2 bind2nd 2.2.3 not1 2.2.4 新型适配器bind 2.3 迭代器适配器 2.3.1 reverse_iterator 2.3.2 insert_iterator 2.4 X适配器 2.4.1 ostream_iterator 2.4.2 istre…

BUUCTF-babyheap_0ctf_2017

checksec 标准堆菜单 IDA Allocate void __fastcall sub_D48(__int64 a1) {int i; // [rsp10h] [rbp-10h]int v2; // [rsp14h] [rbp-Ch]void *v3; // [rsp18h] [rbp-8h]for ( i 0; i < 15; i ){if ( !*(_DWORD *)(24LL * i a1) ){printf("Size: ");v2 sub_1…

【云原生】无VIP稳定性和可扩展性更强的k8s高可用方案讲解与实战操作

文章目录一、概述二、架构三、开始部署1&#xff09;节点信息2&#xff09;前期准备&#xff08;所有节点&#xff09;1、配置hosts2、配置互信3、时间同步4、关闭防火墙5、禁用SELinux6、关闭swap7、设置bridge-nf-call-iptables3&#xff09;安装容器docker&#xff08;所有节…

C++标准库分析总结(十)——<仿函数/函数对象>

目录 1.functor仿函数简介 2 仿函数的分类 3 仿函数使用 4 仿函数可适配的条件 1.functor仿函数简介 仿函数是STL中最简单的部分&#xff0c;存在的本质就是为STL算法部分服务的&#xff0c;一般不单独使用。仿函数&#xff08;functors&#xff09;又称为函数对象&…

Windows 命令行cmd.exe简单介绍

介绍&#xff1a; 在windows系统中&#xff0c;Windows命令shell&#xff08;cmd.exe&#xff09;,在 SystemRoot/System32目录下。 启动命令行&#xff0c;在"开始"——>"搜索"中输入cmd&#xff0c;此时命令行展示当前工作目录&#xff0c;默认为/u…

JS(第二十四课)JS高级Es6语法

ECMAScript 6_百度百科 (baidu.com) 第一部分:百度简介 ECMAScript 6&#xff08;简称ES6&#xff09;是于2015年6月正式发布的JavaScript语言的标准&#xff0c;正式名为ECMAScript 2015&#xff08;ES2015&#xff09;。它的目标是使得JavaScript语言可以用来编写复杂的大型…

mybatis 01: 静态代理 + jdk动态代理 + cglib动态代理

背景 有时目标对象不可直接访问&#xff0c;只能通过代理对象访问 图示&#xff1a; 示例1&#xff1a; 房东 > 目标对象房屋中介 > 代理对象你&#xff0c;我 > 客户端对象示例2&#xff1a; 运营商(电信&#xff0c;移动&#xff0c;联通) > 目标对象第三方公司…

(Java)P1223 排队接水

排队接水 一、 题目描述 有 nnn 个人在一个水龙头前排队接水&#xff0c;假如每个人接水的时间为 TiT_iTi​&#xff0c;请编程找出这 nnn 个人排队的一种顺序&#xff0c;使得 nnn 个人的平均等待时间最小。 二、输入格式 第一行为一个整数 nnn。 第二行 nnn 个整数&…

Linux下加密库Libsodium 使用实践(ip监听、封装的加密消息、运行系统命令)

Libsodium 是一个用C语言编写的库&#xff0c;是一种新的易于使用的高速软件库&#xff0c;用于网络通信、加密、解密、签名等实现和简化密码学。 完成 Libsodium 安装 Libsodium 是一个用于加密&#xff0c;解密&#xff0c;数字签名&#xff0c;密码哈希&#xff0c;等的&a…

java每日一练(4)

java每日一练(4) 文章目录单选部分不定项选择题多选题编程题单选部分 1.下列与队列结构有关联的是&#xff08;&#xff09; A 函数的递归调用   B 数组元素的引用   C 多重循环的执行   D 先到先服务的作业调度 队列的特点 &#xff1a; 先进先出 , 所以 答案非常明显 D  …

【python】通过gitlab v4版本api接口获取所有项目代码示例

目录一、环境信息二、参数说明三、脚本使用说明1. 使用python2运行git.py2. python脚本执行完毕会自动生成如下四个文件3. 其他脚本说明四、脚本源码1. git.py2. update.sh五、脚本扩展说明附录一、环境信息 脚本适用于&#xff1a;python2 测试版本&#xff1a;2.7.18 二、…

于我来说,赌才是世界杯的灵魂~

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 先看这里前言了解足球首看世界杯再看世界杯前言 身边朋友也有踢球的&#xff0c;但是不多。就两个&#xff0c;一个是我同学&#xff0c;一个是我同事…打篮球的倒是不少&#xff0c;猜想…

这五个适合上班族的副业你知道多少

第二职业赚钱的路子有什么&#xff1f;从理论上讲&#xff0c;第二职业就是一个创业的过程&#xff0c;也遵照自主创业一般规律。可是第二职业是在业余时间和没有灵活运用资源挣钱&#xff0c;和创业有所不同。第二职业门坎变低&#xff0c;更比较发达&#xff0c;因此今天小编…

Arduino--音乐频谱

本文主要介绍基于Arduino实现的音乐频谱显示&#xff0c;音乐频谱原理就是声音传感器&#xff08;MIC&#xff09;接收音频信号后通过FFT将时域信号转换成频域信号&#xff0c;再将音频信号频域分量分别显示在对应的LED点阵屏上&#xff0c;呈现出音乐随频律动的感觉&#xff0…

Windows Docker Desktop安装K8S

Docker DeskTop提供了K8S支撑&#xff0c;安装也较为简单。对于本地开发&#xff0c;测试部署项目较为方便。下面进行简单说明。 DockerDesktop配置镜像源&#xff0c;较为简单&#xff0c;有许多网上例子直接参考即可。启用K8S等待一阵子&#xff0c;K8S即可安装成功。可以看…

MySQL存储引擎介绍

首先 我们要知道 什么是引擎 我们常见的 客机 直升机 火箭等等 他们都有自己的引擎 引擎也就是指一个机器的核心 当然 你如果是一个飞机 那你自然是不能用火箭的引擎的 存储引擎就是存储数据 建立索引 更新/查询数据等技术的实现方式&#xff0c;存储引擎是基于表的&#xf…

TDK | CeraLink 电容器快速切换逆变器的革新

本周向大家介绍另一款压电技术的产品CeraLink。 CeraLink 是一系列非常紧凑的电容器&#xff0c;用于稳定直流链路中的电压。因此它们适合用作缓冲器或直流母线电容器。这些产品基于 PLZT 陶瓷&#xff0c;旨在为工程师提供针对快速开关转换器、空间要求非常紧凑的转换器和需要…

Java项目:饰品商城系统(java+SSM+JSP+javascript+jQuery+Mysql)

源码获取&#xff1a;俺的博客首页 "资源" 里下载&#xff01; 项目介绍 本项目分为前台与后台&#xff0c;有普通用户与管理员两种角色&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,用户管理,一级分类管理,二级分类管理,饰品管理,订单管理、发货、…

Linux中对磁盘(硬盘)分区和挂载

记录&#xff1a;346 场景&#xff1a;在CentOS 7.9操作系统上&#xff0c;使用fdisk对磁盘分区&#xff1b;使用mkfs.xfs创建文件系统&#xff1b;使用mount挂载磁盘到目录&#xff1b;使用umount卸载目录已挂载的磁盘&#xff1b;修改文件系统表fstab&#xff0c;满足开机启…

【计算机毕业设计】网上游戏代练商城系统

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 摘要 随着当今社会的发展&#xff0c;时代的进步&#xff0c;各行各业也在发生着变化&#xff0c;本系统健身房这一方面&#xff0c;利用网站游戏代练已经逐步进入人们的生活。传统的网上游戏代练&#xff0c;都是用…