MySQL中的Join 的算法(NLJ、BNL、BKA)

news2025/1/17 23:17:05

本文已收录至Github,推荐阅读 👉 Java随想录

文章目录

    • 摘要
    • 什么是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 Join、Left Join、Right Join、Full 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);

在这个语句里,假设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);

字段b上是没有建立索引的。

这时候,被驱动表上没有可用的索引,算法的流程是这样的:

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

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

可以看到,在这个过程中,对表t1和t2都做了一次全表扫描,因此总的扫描行数是1100。由于join_buffer是以无序数组的方式组织的,因此对表t2中的每一行,都要做100次判断,总共需要在内存中做的判断次数是:100*1000=10万次。

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

join_buffer的大小是由参数join_buffer_size设定的,默认值是256k。如果放不下表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中放入的数据是P1~P100,表示的是只会取查询需要的字段。当然,如果join buffer放不下P1~P100的所有数据,就会把这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;

使用临时表的大致思路是:

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

这样可以大大减少扫描的行数,提升性能。

总结

在MySQL中,不管Join使用的是NLJ还是BNL总是应该使用小表做驱动表。更准确地说,**在决定哪个表做驱动表的时候,应该是两个表按照各自的条件过滤,过滤完成之后,计算参与join的各个字段的总数据量,数据量小的那个表,就是“小表”,应该作为驱动表。**应当尽量避免使用BNL算法,如果确认优化器会使用BNL算法,就需要做优化。优化的常见做法是,给被驱动表的join字段加上索引,把BNL算法转成BKA算法。对于不好在索引的情况,可以基于临时表的改进方案,提前过滤出小数据添加索引。

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

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

相关文章

【概念大全(关系,码,选择,投影,连接,运算)】第二章 关系数据库

第二章 关系数据库 1. 关系的基本概念1. 什么是域2. 笛卡尔积3. 笛卡尔积中 有意义的子集 就是关系4. 候选码 &#xff08;是唯一标识符 并不是用 只有一个进行判断&#xff09;5. 全码&#xff08;一行中都不重复&#xff09;6. 主码&#xff08;候选码选一个就是主码&#xf…

手术麻醉临床信息系统源码,实时自动采集麻醉和监护设备的数据

手术麻醉临床信息系统源码 手术麻醉临床信息系统实时采集麻醉和监护设备的数据&#xff0c;实现术前、术中、术后全手术过程的数字化管理&#xff0c;为手术室提供全数字化的业务管理、临床管理、费用管理、材料管理等。同时通过与 HIS、EMR、PACS、LIS 等系统无缝集成&#x…

Linux中信号的基础知识

信号的概念 Linux操作系统中&#xff0c;信号是一种进程间通信&#xff08;Inter-Process Communication, IPC&#xff09;机制&#xff0c;用于向其他进程发送通知或指示&#xff0c;通常是为了通知特定事件的发生&#xff0c;如程序终止、用户按下特定按键等。信号提供了一种…

java获取输入内容的方法

Java中的对象类型可以有多种&#xff0c;比如 Object、 StringBuilder等&#xff0c;其中 Object和 String是最常用的对象类型&#xff0c;而 StringBuilder类是一种特殊的类&#xff0c;它能通过继承来创建其他的对象。 我们在平时的工作中经常会遇到需要获取输入内容的情况&a…

界面控件DevExpress Blazor UI v22.2亮点:全新的Window组件

DevExpress拥有.NET开发需要的所有平台控件&#xff0c;包含600多个UI控件、报表平台、DevExpress Dashboard eXpressApp 框架、适用于 Visual Studio的CodeRush等一系列辅助工具&#xff0c;该组件拥有众多新产品和数十个具有高影响力的功能&#xff0c;可为桌面、Web和移动应…

详解async 与 await,带您理解Playwright使用异步方法的正确姿势!

大家在使用python做playwright自动化测试的过程中&#xff0c;一定会发现下面这种异步用法 async def func():await apiawait api 很多同学可能只是按照这种写法来编写项目的自动化测试代码&#xff0c;对于具体细节可能并不了解&#xff0c;今天我就来讲一下playwright异步用…

基于fNIRS的脑功能连接分析:图论方法

导读 背景&#xff1a;fNIRS是一种利用近红外光谱进行功能神经成像的光学脑监测技术。它使用近红外光来测量大脑活动&#xff0c;并估计由于运动活动而引起的大脑皮层血流动力学活动。fNIRS通过光学吸收来测量含氧和脱氧血红蛋白中氧水平的变化。多源噪声和伪影干扰导致的信号…

【P6】JMeter HTTP Cookie管理器

文章目录 一、测试网站二、Cookie 设置规则2.1、无配置元件时&#xff0c;Cookie 不会自动设置&#xff08;与线程组设置无关&#xff09;2.2、有配置元件&#xff0c;不选任何参数时&#xff0c;Cookie 自动设置&#xff08;与线程组设置无关&#xff09;2.3、有配置元件&…

Java——二叉搜索树中第k小的元素

题目链接 leetcode在线oj题——二叉搜索树中第k小的元素 题目描述 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 个最小元素&#xff08;从 1 开始计数&#xff09;。 题目示例 示例1 输入&#xff1a;root [3,1…

软件工程本科生毕业论文中常见问题总结

文章目录 目录结构不合理 绪论&#xff08;引言&#xff09;研究内容 表格表格首行不要加粗表格能不跨页的就不要跨页 其他常见格式问题专有名词要用统一写法 首先先仔细阅读&#xff1a; 本科生毕业论文&#xff08;设计&#xff09;写作与排版打印规范 目录 结构不合理 2.…

Ubuntu 增加swap交换内存

一、创建虚拟内存 在实际开发中发现swap交换分区不够用了&#xff0c;于是需要创建虚拟内存来增加交换分区的大小。 在系统空闲空间位置创建swap虚拟内存专用文件夹 cd /data //切到你想要创建交换分区的目录 mkdir swap //新建文件夹swap cd swap //进入swap文件夹 备…

Fastjson<1.2.48远程代码执行漏洞(CNVD-2019-22238)

漏洞存在原因 在fastjson<1.2.24版本中&#xff0c;在解析json的过程中&#xff0c;支持使用autoType来实例化某一个具体的类&#xff0c;并调用该类的set/get方法来访问属性。而在1.24<fastjson<1.2.48版本中后增加了反序列化白名单&#xff0c;而在1.2.48以前的版本…

【容器化应用程序设计和开发】2.4 容器网络和存储

往期回顾&#xff1a; 第一章&#xff1a;【云原生概念和技术】 第二章&#xff1a;2.1 容器化基础知识和Docker容器 第二章&#xff1a;2.2 Dockerfile 的编写和最佳实践 第二章&#xff1a;2.3 容器编排和Kubernetes调度 2.4 容器网络和存储 容器网络和存储是容器化应用…

操作系统第二章——进程与线程(下)

东风夜放花千树&#xff0c;更吹落&#xff0c;星如雨 文章目录 2.3.1 进程同步&#xff0c;进程互斥知识总览什么是进程同步什么是进程互斥知识回顾 2.3.2 进程互斥的软件实现方法知识总览如果没有进程互斥单标志法双标志先检查法双标志后检查法Peterson算法知识回顾 2.3.3进程…

Linkage Mapper解密数字世界链接 专栏内容介绍

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Linkage Mapper解密数字世界链接 在数字时代&#xff0c;链接是信息的核心&#xff0c;链接地…

typescript:熟练掌握typescript

一、简介 TypeScript 教程 | 菜鸟教程 TypeScript (简称:TS)是JavaScript的超集 (JS有的TS 都有)。 TypeScriptType JavaScript (在JS 基础之上&#xff0c;为JS添加了类型支持)。 哔哩哔哩_教程_TypeScript 二、TypeScript为什么要为js增加类型支持&#xff1f; 背景&am…

Flowable+React+bpmn-js实现工作流

由于新东家使用的是React&#xff0c;不是Vue&#xff0c;而自己一直想做一个关于工作流的应用出来&#xff0c;断断续续&#xff0c;花了几个月的时间&#xff0c;开发了工作流的功能&#xff0c;后面会继续完善。 技术栈 前端 前端是基于React开发的&#xff0c;使用了ant…

【LeetCode】704.二分查找

704.二分查找 解析&#xff1a; 思路一&#xff1a;暴力解法&#xff0c;直接遍历&#xff0c;从头开始查找&#xff0c;如果找到直接返回下标&#xff0c;找不到返回-1。 class Solution { public:int search(vector<int>& nums, int target) {for(int i 0; i <…

[架构之路-192]-《软考-系统分析师》-8-软件工程 - 14种UML图快速概览

目录 第1章 UML概述 1.1 什么是UML&#xff1f; 1.2 为什么要用UML&#xff1f; 1.3 UML图有哪些&#xff1f; 1.4 UML图概览 第2章 UML图示 2.1 静态图、结构图 - 什么是类图&#xff1f; 泛化&#xff08;Generalization&#xff09; 实现&#xff08;Realization&a…

四:redis的常见命令及5种基本数据类型

四:redis的常见命令及数据类型 Redis 键(key) 命令1.String&#xff08;字符串&#xff09;2.List(列表类型)3.set(集合)4.Hash(哈希)5.Zset(有序集合) redis官网可查看所有命令&#xff1a; https://www.redis.net.cn/order/ Redis 键(key) 命令 127.0.0.1:6379> keys * …