【MySQL】深入理解MySQL中的Join算法

原创不易,注重版权。转载请注明原作者和原文链接

文章目录

    • 什么是Join
    • Index Nested-Loop Join
    • Block Nested-Loop Join
    • MRR & BKA
    • 总结

在数据库处理中,Join操作是最基本且最重要的操作之一,它能将不同的表连接起来,实现对数据集的更深层次分析。

MySQL作为一款流行的关系型数据库管理系统,其在执行Join操作时使用了多种高效的算法,包括Index Nested-Loop Join(NLJ)和Block Nested-Loop Join(BNL)。这些算法各有优缺点,本文将探讨这两种算法的工作原理,以及如何在MySQL中使用它们。

什么是Join

在MySQL中,Join是一种用于组合两个或多个表中数据的查询操作。Join操作通常基于两个表中的某些共同的列进行,这些列在两个表中都存在。MySQL支持多种类型的Join操作,如Inner JoinLeft JoinRight Join等。

Inner Join是最常见的Join类型之一。在Inner Join操作中,只有在两个表中都存在的行才会被返回。

例如,如果我们有一个“customers”表和一个“orders”表,我们可以通过在这两个表中共享“customer_id”列来组合它们的数据。

SELECT *
FROM customers
INNER JOIN orders
ON customers.customer_id = orders.customer_id;

上面的查询将返回所有存在于“customers”和“orders”表中的“customer_id”列相同的行。

Index Nested-Loop Join

Index Nested-Loop Join(NLJ)算法是Join算法中最基本的算法之一。

在NLJ算法中,MySQL首先会选择一个表(通常是小型表)作为驱动表,并迭代该表中的每一行。然后,MySQL在第二个表中搜索匹配条件的行,这个搜索过程通常使用索引来完成。一旦找到匹配的行,MySQL将这些行组合在一起,并将它们作为结果集返回。

工作流程如图:

例如,执行下面这个语句:

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

注:当使用 straight_join 时,MySQL会强制按照在查询中指定的从左到右的顺序执行连接。

在这个语句里,假设 t1 是驱动表,t2 是被驱动表。我们来看一下这条语句的explain结果。

可以看到,在这条语句里,被驱动表t2的字段a上有索引,join过程用上了这个索引,因此这个语句的执行流程是这样的:

  1. 从表t1中读入一行数据 R;
  2. 从数据行R中,取出a字段到表t2里去查找;
  3. 取出表t2中满足条件的行,跟R组成一行,作为结果集的一部分;
  4. 重复执行步骤1到3,直到表t1的末尾循环结束。

这个过程就跟我们写程序时的嵌套查询类似,并且可以用上被驱动表的索引,所以我们称之为「Index Nested-Loop Join」,简称NLJ

NLJ是使用上了索引的情况,那如果查询条件没有使用到索引呢?

MySQL会选择使用另一个叫作「Block Nested-Loop Join」的算法,简称BNL

Block Nested-Loop Join

Block Nested Loop Join(BNL)算法与NLJ算法不同的是,BNL算法使用一个类似于缓存的机制,将表数据分成多个块,然后逐个处理这些块,以减少内存和CPU的消耗。

例如,执行下面这个语句:

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

如果 t2 表的字段b上是没有建立索引的。这时候,被驱动表上没有可用的索引,算法的流程是这样的:

  1. 把表t1的数据读入线程内存join_buffer中,由于我们这个语句中写的是select *,因此是把整个表t1放入了内存;
  2. 扫描表t2,把表t2中的每一行取出来,跟join_buffer中的数据做对比,满足join条件的,作为结果集的一部分返回。

这条SQL语句的explain结果如下所示:

可以看到,在这个过程中,MySQL对表 t1 和 t2 都做了一次全表扫描,因此总的扫描行数是1100。

由于join_buffer是以无序数组的方式组织的,因此对表t2中的每一行,都要做100次判断,总共需要在内存中做的判断次数是:100*1000=10万次

虽然Block Nested-Loop Join算法是全表扫描。但是是在内存中进行的判断操作,速度上会快很多。但是性能仍然不如NLJ。

join_buffer的大小是由参数join_buffer_size设定的,默认值是256k。

那如果join_buffer_size的大小不足以放下表t1的所有数据呢?

办法很简单,就是分段放,执行流程如下:

  1. 顺序读取数据行放入join_buffer中,直到join_buffer满了。
  2. 扫描被驱动表跟join_buffer中的数据做对比,满足join条件的,作为结果集的一部分返回。
  3. 清空join_buffer,重复上述步骤。

虽然分成多次放入join_buffer,但是判断等值条件的次数还是不变的,依然是10万次。

MRR & BKA

上篇文章里我们有提到MRR(Multi-Range Read)。MySQL在5.6版本后引入了 Batched Key Acess(BKA) 算法,这个BKA算法,其实就是对NLJ算法的优化,而BKA算法正是基于MRR。

NLJ算法执行的逻辑是:从驱动表t1,一行行地取出a的值,再到被驱动表t2去做join。也就是说,对于表t2来说,每次都是匹配一个值。这时,MRR的优势就用不上了

其实我们可以从表t1里一次性地多拿些行出来,先放到一个临时内存,一起传给表t2。这个临时内存不是别人,就是join_buffer

通过上一篇文章,我们知道join_buffer 在BNL算法里的作用,是暂存驱动表的数据。但是在NLJ算法里并没有用。那么,我们刚好就可以复用join_buffer到BKA算法中。

NLJ算法优化后的BKA算法的流程,如图所示:

图中,在join_buffer中放入的数据是R1~R100,表示的是只会取查询需要的字段。当然,如果join buffer放不下R1~R100的所有数据,就会把这100行数据分成多段执行上图的流程。

如果要使用BKA优化算法的话,你需要在执行SQL语句之前,先设置

set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';

其中,前两个参数的作用是要启用MRR。这么做的原因是,BKA算法的优化要依赖于MRR。

对于BNL,我们可以通过建立索引转为BKA。但是,有时候你确实会碰到一些不适合在被驱动表上建索引的情况。比如下面这个语句:

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

假设t1表1000行,t2表100万行,t2.b<=2000过滤后,t2表需要参与join的只有2000行数据。

如果这条语句是一个低频的SQL语句,那么在表t2的字段b上创建索引就很浪费了。

这时候,我们可以考虑使用临时表。使用临时表的大致思路是:

  1. 把表t2中满足条件的数据放在临时表tmp_t中;
  2. 为了让join使用BKA算法,给临时表tmp_t的字段b加上索引;
  3. 让表t1和tmp_t做join操作。

此时,对应的SQL语句的写法如下:

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);

总体来看,不论是在原表上加索引,还是用有索引的临时表,我们的思路都是让join语句能够用上被驱动表上的索引,来触发BKA算法,提升查询性能。

总结

在MySQL中,不管Join使用的是NLJ还是BNL总是应该使用小表做驱动表。更准确地说,在决定哪个表做驱动表的时候,应该是两个表按照各自的条件过滤,过滤完成之后,计算参与join的各个字段的总数据量,数据量小的那个表,就是“小表”,应该作为驱动表

另外应当尽量避免使用BNL算法,如果确认优化器会使用BNL算法,就需要做优化。优化的常见做法是,给被驱动表的join字段加上索引,把BNL算法转成BKA算法。对于不好在索引的情况,可以基于临时表的改进方案,提前过滤出小数据添加索引。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端八股文

目录 一、CSS1.说一下CSS的盒模型。2.CSS选择器的优先级&#xff1f;3.隐藏元素的方法有哪些&#xff1f;4.px和rem的区别是什么&#xff1f;5.重绘重排有什么区别&#xff1f;6.让一个元素水平垂直居中的方式有哪些&#xff1f;7.CSS的哪些属性哪些可以继承&#xff1f;哪些不…

vscode 调试使用 make 编译的项目

1、首先点击运行 --> 启动调试&#xff1a; 2、选择g或gcc生成和调试活动文件&#xff1a; 3、出现下面提示是正常的&#xff0c;点击仍要调试&#xff1a; 点击打开“launch.json”&#xff1a; 4、此时会在项目工作目录下生成tsak.josn和launch.json文件&#xff1a; 如…

Android rtmp 低延迟直播方案:简介

Android rtmp 低延迟直播方案:简介 Android RTMP 低延迟直播方案:使用 RTMP 推送至 ZLMediaKit,通过 WebRTC 进行拉流。

【eNSP】VLAN基础配置

一、基于接口划分VLAN&#xff08;Access接口和Trunk接口&#xff09; 1、创建VLAN LSW1 [LSW1]vlan batch 10 20 Info: This operation may take a few seconds. Please wait for a moment...done.LSW2 [LSW2]vlan batch 10 20 Info: This operation may take a few second…

【网络】UDP和TCP套接字编程

目录 一、初始ip地址和port二、网络字节序三、socket编程1、sockaddr结构2、socket编程接口2.1、创建 socket接口2.2、绑定端口号2.3、监听socket2.4、接收请求2.5、建立连接2.6、收消息2.7、发消息 3、UDP套接字编程 -- 现实大小写转换4、TCP套接字编程 -- 原生多线程现实TCP通…

若依微服务前后端部署启动流程(只记录)

若依官网&#xff1a;https://www.ruoyi.vip/ 若依源码下载&#xff0c;直接zip既可&#xff1a;RuoYi-Cloud: &#x1f389; 基于Spring Boot、Spring Cloud & Alibaba的分布式微服务架构权限管理系统&#xff0c;同时提供了 Vue3 的版本 下载解压&#xff0c;导入 idea&…

从零开始学习 Java:简单易懂的入门指南之IO序列化、打印流、压缩流(三十三)

序列化、打印流、压缩流、工具包 1. 序列化1.1 概述1.2 ObjectOutputStream类构造方法序列化操作 1.3 ObjectInputStream类构造方法反序列化操作1反序列化操作2 1.4 练习&#xff1a;序列化集合案例分析案例实现 2. 打印流2.1 概述2.2 PrintStream类构造方法改变打印流向 3. 压…

Docker Mysql实战:docker compose 搭建Mysql

1、docker-compose-mysql文件准备 进入/home/docker目录&#xff0c;新建docker-compose-mysql.yml文件&#xff0c;内容如下&#xff1a; version: 3.0 services:mysql:image: "mysql:5.7"container_name: "mysql"environment:MYSQL_ROOT_PASSWORD: &q…

战神引擎传奇假设教程

战神引擎传奇假设教程 --------------------------------------------------------------------------------------------------- 传奇这款游戏可以说是一代人的回忆&#xff0c;特别是8090后&#xff0c;传奇对他们有着许许多多的难忘的回忆&#xff0c; 随着时代的发展&…

【内网穿透】Docker部署Drupal并实现公网访问

目录 前言 1. Docker安装Drupal 2. 本地局域网访问 3 . Linux 安装cpolar 4. 配置Drupal公网访问地址 5. 公网远程访问Drupal 6. 固定Drupal 公网地址 前言 Dupal是一个强大的CMS&#xff0c;适用于各种不同的网站项目&#xff0c;从小型个人博客到大型企业级门户网站。…

win32汇编源程序结构

先看一个实例&#xff1a; ; 使用 Win32ASM 写的 Hello, world 程序 ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&g…

three.js学习之vR展厅

目标 1、需要会的知识点three.js的场景&#xff0c;摄像机&#xff0c;渲染器&#xff0c;轨道控制器&#xff0c;坐标轴&#xff0c;场景适配&#xff0c;渲染循环创建立方缓冲几何体、纹理、3d物体 实现&#xff1a;创建立方几何体&#xff0c;纹理贴图镜面反向渲染&#xf…

Springboot使用sqlcipher4加密sqlite数据库

在有些业务场景&#xff0c;需要使用sqlite数据库&#xff0c;但sqlite数据库生的db文件&#xff0c;是明文的&#xff0c;该文件被别人拿到&#xff0c;就可以看到里面的所有数据&#xff0c;非常不安全&#xff0c;市面上有很多对sqlite数据库文件加密的方式&#xff0c;但都…

vscode远程ssh服务器且更改服务器别名

目录 1、打开VS Code并确保已安装"Remote - SSH"扩展。如果尚未安装&#xff0c;请在扩展市场中搜索并安装它。 2、单击左下角的"Remote Explorer"图标&#xff0c;打开远程资源管理器。 3、在远程资源管理器中&#xff0c;单击右上角的齿轮图标&#x…

1712A 300A嵌入式电源系统

1712A 300A嵌入式电源系统 1712A 300A嵌入式电源系统采用模块化设计、组合式结构&#xff0c;由控制器、整流模块、交流配电单元、直流配电单元等组成。该系统将交流电转换成稳定的-48V直流电&#xff0c;用于铁塔、移动、电信、联通等公司的传输、接入网&#xff0c;以及专网等…

vscode 连接ubuntu git下载缓慢

在ubuntu20.04下载&#xff1a; git clone https://github.com/introlab/rtabmap.git src/rtabmap 挂掉情况 export https_proxyhttp://10.10.10.176:7890export http_proxyhttp://10.10.10.176:7890 其中 10.10.10.176是我本机的ip地址&#xff0c;7890是我的代理后几位 如…

【PPT】ppt里面使用svg图标

要想编辑好的PPT&#xff0c;少不了小图标的美化&#xff0c;图标可以使PPT变得更有趣&#xff0c;更易懂&#xff0c;更美观。 对于png&#xff0c;主要处理它的颜色&#xff0c;可使用【重新着色】功能。 对于jpg&#xff0c;主要处理它的背景&#xff0c;删除背景后同png处…

JSONUtil.parse将java对象转为json时,需要在java对象中设置get、set方法

想要使用JSONUtil.parse将java对象转为json格式&#xff0c;但是一直为空&#xff0c;代码如下 public class MyTest {public static void main(String[] args) {Test3<String> test3 new Test3<>("2","hhhhhhaaa");System.out.println(JSON…

UI设计师岗位的基本职责八篇

UI设计师岗位的基本职责1 职责&#xff1a; 1. 负责公司互联网产品app、web、h5等的用户界面设计工作; 2. 负责运营活动相关的平面及视频设计支持; 3. 负责完成产品相关的界面、图标、动画等的图形界面设计&#xff0c;并参与制定、编写产品视觉设计规范文档; 4. 整理和分…

Oracle 云服务即将支持 PostgreSQL!

2023 年 9 月 19 日&#xff0c;Oracle 产品团队发布了一篇文章&#xff0c;宣布 Oracle 云基础架构&#xff08;OCI&#xff09;开始提供 PostgreSQL 服务。目前支持的版本为 PostgreSQL 14.9&#xff0c;提供有限支持&#xff0c;12 月份将会提供正式版本。 众所周知&#x…