数据库的JOIN连接查询算法

文章目录

    • 3.2 Join 算法优化
      • 3.1.2 Nested Loop Join(NLJ)
      • 3.1.3 Block Nested Loop Join(BNLJ)
      • 3.1.4 Index Nested Loop Join(INLJ)
      • 3.1.5 Sort Merge Join(SMJ)
      • 3.1.6 Hash Join

3.2 Join 算法优化

JOIN算法指的是在执行SQL查询语句中,当涉及到两个或多个表之间的数据连接(JOIN)时,查询优化器用来决定如何最有效地从这些表中检索和组合数据的方法,选择最适合的JOIN算法。

3.1.2 Nested Loop Join(NLJ)

嵌套循环连接(Nested Loop Join)是一种最基本的连接实现算法。它先从外部表(驱动表)中获取满足条件的数据,并对每个行再遍历一次另一个表(内层表)以找到匹配的行。

假设我们有两个表 employeesdepartments,并且我们要找出每个员工所在的部门名称。如果使用 NLJ,MySQL 将会遍历 employees 表中的每一行,并且针对每一行遍历 departments 表中的所有行,直到找到一个与当前员工的部门。

SQL如下:

SELECT e.name, d.name
FROM employees e
JOIN departments d ON e.department_id = d.id;

对于左表中的每一行,遍历右表的所有行寻找匹配的记录。这是一种最基本的连接方式,适用于小规模数据集或当没有合适的索引可用时。

如图所示:

伪代码如下:

-- 先遍历外部表
FOR each row e IN employees-- 外部表的每一条数据都会再遍历一遍内部表FOR each row d IN departments-- 最终寻找合适的数据IF e.department_id == d.id THENOUTPUT (e.name, d.name)

3.1.3 Block Nested Loop Join(BNLJ)

Block Nested Loop 是对 NLJ 的一种改进,它利用内存中的缓存块来减少磁盘 I/O 操作。不是每次都读取整个右表(被驱动表),而是每次从右表中加载一部分数据到内存(块),然后用左表(驱动表)的一行去匹配这些块里的所有行。

BNLJ 与 NLJ 类似,但 MySQL 会尝试将尽可能多的 departments 行加载到内存中,然后用 employees 表的每一行去匹配这些已经加载到内存中的 departments 行。

同样是如下SQL:

SELECT e.name, d.name
FROM employees e
JOIN departments d ON e.department_id = d.id;

BNLJ 是 NLJ 的一种优化版本,它试图减少不必要的磁盘 I/O 操作。它的核心思想是分批加载右表的数据到内存中,然后用左表的一行去匹配这些已经加载到内存中的块。具体来说,不是每次都扫描整个右表,而是每次只加载一部分数据(一个或多个块),并且尽可能多地利用内存中的缓存。

如图所示:

伪代码如下:

// 假设有一个游标指向employees表
CURSOR cursor = OPEN CURSOR FOR SELECT * FROM employees;WHILE cursor has more rows // 当游标还有更多行可处理LOAD a block of rows from departments into memory as BLOCK // 加载一部分部门记录到内存中作为BLOCKFETCH NEXT ROW FROM cursor INTO e // 获取下一行员工记录eWHILE e is not null AND there are still rows in BLOCK to process // 当有未处理的员工记录和部门块FOR each row d IN BLOCK // 遍历当前加载到内存的部门记录dIF e.department_id == d.id THEN // 如果找到匹配的部门IDOUTPUT (e.name, d.name) // 输出匹配的结果FETCH NEXT ROW FROM cursor INTO e // 获取下一行员工记录e
CLOSE cursor; // 关闭游标

BNLJ 关键点在于,当 BNLJ 从磁盘加载一批数据到内存后,它可以重复使用这批数据来与左表的多行进行比较,从而减少了频繁访问磁盘的需求。这样做的前提是内存足够大,可以容纳下右表的一个或多个块,以及左表的当前行。

3.1.4 Index Nested Loop Join(INLJ)

当右表有一个可以被有效使用的索引时,MySQL 可以直接通过索引来查找匹配的行,而不需要扫描整个表。这通常比简单的 NLJ 更快,因为它减少了需要访问的行数。

例如:如果我们为 departments 表上的 id 列创建了索引,那么 MySQL 可以直接根据 employees.department_id 查找 departments 表中的相应记录,而不是扫描整个表。

同样是如下SQL:

SELECT e.name, d.name
FROM employees e
JOIN departments d ON e.department_id = d.id;

利用右表上的索引来快速查找匹配的记录。这可以极大地提高性能,尤其是在右表很大而左表相对较小的情况下。

如图所示:

伪代码如下:

CREATE INDEX idx_department ON departments(id); // 假设已经存在这个索引FOR each row e IN employees // 外层循环遍历左表LOOKUP rows FROM departments USING idx_department WHERE id = e.department_id // 使用索引查找FOR each matching row d // 遍历匹配的结果OUTPUT (e.name, d.name)

3.1.5 Sort Merge Join(SMJ)

在Sort Merge Join中,首先两个表按连接键排序,之后同时遍历这两个有序列表,如果两个元素的连接键相当,则匹配成功。如果不相等则指向较小的连接键值,找到匹配的记录。

例如:如果 employeesdepartments 都是根据 department_idid 排序的,那么 MySQL 可以简单地同时遍历两个表,只比较相等的键值,从而高效地完成 JOIN。

如图所示:

伪代码如下:

SORT employees BY department_id;		// 根据 department_id 字段将 employees 表排序
SORT departments BY id;					// 根据 id 字段将 departments 表排序// 开始合并两个已排序的列表,基于 e.department_id 和 d.id 的匹配
MERGE SORTED employees AND sorted departments ON e.department_id = d.id// 在处理两个排序后的列表时WHILE processing both sorted lists// 如果当前员工的部门ID等于当前部门的IDIF e.department_id == d.id THEN// 输出员工的名字和对应部门的名字OUTPUT (e.name, d.name)// 如果当前员工的部门ID小于当前部门的IDELSE IF e.department_id < d.id THEN// 移动到下一个员工记录,因为当前员工所属的部门已经处理完毕或不存在于部门列表中ADVANCE TO NEXT e// 如果当前员工的部门ID大于当前部门的IDELSE// 移动到下一个部门记录,因为当前部门没有对应的员工或者所有相关员工已经被处理ADVANCE TO NEXT d

3.1.6 Hash Join

Hash Join 在MySQL 8.0.18版本引入,且不需要索引的支持。Hash Join在查询时首先会在内存中构建一个哈希表,然后用另一个表的数据去探测这个哈希表,检查该表中的每一行是否存在于哈希表中。这种 JOIN 方式非常适合大规模数据集,对于等值连接特别有效,尤其是在内存足够大的情况下。

Hash Join的工作流程:

  • 1)**Build Phase (构建阶段): **
    • 选择较小的表作为构建表(Build Table),并在内存中为该表构建一个哈希表。
    • 对构建表中的每一行计算哈希函数,并将结果插入到这个哈希表中。这个哈希表的键是连接列上的哈希值,而值是指向该行的指针或行本身。
  • 2)**Probe Phase (探测阶段): **
    • 遍历较大的表(Probe Table)中的每一行。
    • 对于每行,使用相同的哈希函数计算连接列的哈希值。
    • 使用这个哈希值在哈希表中查找匹配项。如果找到匹配,则输出这两行组成的连接结果。

Hash Join工作示意图如下:

伪代码如下:

// 构建阶段: 为较小的表创建哈希表(假设部门表较小)
HASH_TABLE ht;
FOR each row d IN departments // 构建表为 departmentsHASH_VALUE hv = HASH(d.id); // 计算哈希值INSERT INTO ht WITH key = hv AND value = d; // 将部门记录插入哈希表// 探测阶段: 扫码较大的表并在哈希表中查询
FOR each row e IN employees // 探测表为 employeesHASH_VALUE hv = HASH(e.department_id); // 使用相同的哈希函数计算哈希值IF ht CONTAINS key = hv THEN // 查找哈希表FOR each matching row d IN ht[hv] // 如果有多个匹配(如哈希冲突)IF e.department_id == d.id THEN // 确认实际值是否相等OUTPUT (e.name, d.name) // 输出匹配的结果

Hash Join 通过构建一个哈希表包含来自一个表的连接列的值,然后扫描另一个表,检查该表中的每一行是否存在于哈希表中。这种 JOIN 方式非常适合大规模数据集和等值连接条件。

默认配置时,MySQL 所有可能的情况下都会使用 hash join。同时提供了两种控制是否使用 hash join 的方法:

  • 1)在全局或者会话级别设置服务器系统变量 optimizer_switch 中的 hash_join=on 或者 hash_join=off 选项。默认为 hash_join=on。
-- 查询优化器相关参数
show variables like 'optimizer_switch';-- 查看hash_join参数,默认为no(开启Hash Join)
hash_join=on
  • 2)MySQL 8.0.18 支持使用hint,在语句级别为特定的连接指定优化器提示 HASH_JOIN 或者 NO_HASH_JOIN(在 8.0.19 和之后的版本中,这些参数不再起作)。
explain select SQL_NO_CACHE * from emp e inner join dept d on e.dept_id=d.id

Tips:可以通过系统变量 join_buffer_size 控制 hash join 允许使用的内存数量;hash join 不会使用超过该变量设置的内存数量。如果 hash join 所需的内存超过该阈值,MySQL 将会在磁盘中执行操作。需要注意的是,如果 hash join 无法在内存中完成,并且打开的文件数量超过系统变量 open_files_limit 的值,连接操作可能会失败。

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

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

相关文章

Golang Gin系列-8:单元测试与调试技术

在本章中&#xff0c;我们将探讨如何为Gin应用程序编写单元测试&#xff0c;使用有效的调试技术&#xff0c;以及优化性能。这包括设置测试环境、为处理程序和中间件编写测试、使用日志记录、使用调试工具以及分析应用程序以提高性能。 为Gin应用程序编写单元测试 设置测试环境…

二叉树的最大深度(C语言详解版)

一、摘要 嗨喽呀大家&#xff0c;leetcode每日一题又和大家见面啦&#xff0c;今天要讲的是104.二叉树的最大深度&#xff0c;思路互相学习&#xff0c;有什么不足的地方欢迎指正&#xff01;好啦让我们开始吧&#xff01;&#xff01;&#xff01; 二、题目简介 给定一个二…

开发环境搭建-3:配置 nodejs 开发环境 (fnm+ node + pnpm)

在 WSL 环境中配置&#xff1a;WSL2 (2.3.26.0) Oracle Linux 8.7 官方镜像 node 官网&#xff1a;https://nodejs.org/zh-cn/download 点击【下载】&#xff0c;选择想要的 node 版本、操作系统、node 版本管理器、npm包管理器 根据下面代码提示依次执行对应代码即可 基本概…

HTB:Support[WriteUP]

目录 连接至HTB服务器并启动靶机 信息收集 使用rustscan对靶机TCP端口进行开放扫描 将靶机TCP开放端口号提取并保存 使用nmap对靶机TCP开放端口进行脚本、服务扫描 使用nmap对靶机TCP开放端口进行漏洞、系统扫描 使用nmap对靶机常用UDP端口进行开放扫描 使用ldapsearch…

洛谷P1017 [NOIP2000 提高组] 进制转换

题目链接&#xff1a;P1017 [NOIP2000 提高组] 进制转换 - 洛谷 | 计算机科学教育新生态 题目难度&#xff1a;普及一 题目分析&#xff1a;这是道数学题&#xff0c;我们都知道&#xff0c;首先按照10进制转成n进制的做法&#xff1a;对这个数不断除以n&#xff0c;将余数一一…

php代码审计2 piwigo CMS in_array()函数漏洞

php代码审计2 piwigo CMS in_array()函数漏洞 一、目的 本次学习目的是了解in_array()函数和对项目piwigo中关于in_array()函数存在漏洞的一个审计并利用漏洞获得管理员帐号。 二、in_array函数学习 in_array() 函数搜索数组中是否存在指定的值。 in_array($search,$array…

【2024年华为OD机试】(A卷,200分)- 查找树中元素 (JavaScriptJava PythonC/C++)

一、问题描述 题目解析 题目描述 题目要求根据输入的坐标 (x, y) 在树形结构中找到对应节点的内容值。其中: x 表示节点所在的层数,根节点位于第0层,根节点的子节点位于第1层,依此类推。y 表示节点在该层内的相对偏移,从左至右,第一个节点偏移为0,第二个节点偏移为1,…

WPS数据分析000006

一、排序 开始→ 排序 同文件→选项→自定义序列→输入序列 二、筛选 高级筛选 条件区域要与列表区域一样。 三、条件格式

基于微信小程序的英语学习交流平台设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

记录一个连不上docker中的mysql的问题

引言 使用的debian12,不同发行版可能有些许差异&#xff0c;连接使用的工具是navicat lite 本来是毫无思绪的&#xff0c;以前在云服务器上可能是防火墙的问题&#xff0c;但是这个桌面环境我压根没有使用防火墙。 直到 ying192:~$ mysql -h127.0.0.1 -uroot ERROR 1045 (28…

SpringBoot基础概念介绍-数据源与数据库连接池

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 毛毛张今天介绍的SpringBoot中的基础概念-数据源与数据库连接池&#xff0c;同时介绍SpringBoot整合两种连接池的教程 文章目录 1 数据库与数据库管理系统2 JDBC与数…

深度学习 Pytorch 单层神经网络

神经网络是模仿人类大脑结构所构建的算法&#xff0c;在人脑里&#xff0c;我们有轴突连接神经元&#xff0c;在算法中&#xff0c;我们用圆表示神经元&#xff0c;用线表示神经元之间的连接&#xff0c;数据从神经网络的左侧输入&#xff0c;让神经元处理之后&#xff0c;从右…

GCC之编译(8)AR打包命令

GCC之(8)AR二进制打包命令 Author: Once Day Date: 2025年1月23日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章请查看专栏: Linux实践记录_Once-Day的博客-C…

SpringBoot统一功能处理

一.拦截器 1.拦截器的简单介绍 拦截器是Spring框架提供的核⼼功能之⼀,主要⽤来拦截⽤⼾的请求,在指定⽅法前后,根据业务需要执⾏预先设定的代码. 2.使用 (i).定义拦截器&#xff1a; (ii).注册拦截器 (iii).拦截路径 (iv).实行流程 3.登录校验 4.DispatcherServlet源码&…

31、Java集合概述

目录 一.Collection 二.Map 三.Collection和Map的区别 四.应用场景 集合是一组对象的集合&#xff0c;它封装了对象的存储和操作方式。集合框架提供了一组接口和类&#xff0c;用于存储、访问和操作这些对象集合。这些接口和类定义了不同的数据结构&#xff0c;如列表、集合…

Unity|小游戏复刻|见缝插针1(C#)

准备 创建Scenes场景&#xff0c;Scripts脚本&#xff0c;Prefabs预制体文件夹 修改背景颜色 选中Main Camera 找到背景 选择颜色&#xff0c;一种白中透黄的颜色 创建小球 将文件夹里的Circle拖入层级里 选中Circle&#xff0c;位置为左右居中&#xff0c;偏上&…

Word 中实现方框内点击自动打 √ ☑

注&#xff1a; 本文为 “Word 中方框内点击打 √ ☑ / 打 ☒” 相关文章合辑。 对第一篇增加了打叉部分&#xff0c;第二篇为第一篇中方法 5 “控件” 实现的详解。 在 Word 方框内打 √ 的 6 种技巧 2020-03-09 12:38 使用 Word 制作一些调查表、检查表等&#xff0c;通常…

Android Studio:视图绑定的岁月变迁(2/100)

一、博文导读 本文是基于Android Studio真实项目&#xff0c;通过解析源码了解真实应用场景&#xff0c;写文的视角和读者是同步的&#xff0c;想到看到写到&#xff0c;没有上帝视角。 前期回顾&#xff0c;本文是第二期。 private Unbinder mUnbinder; 只是声明了一个 接口…

第13章 深入volatile关键字(Java高并发编程详解:多线程与系统设计)

1.并发编程的三个重要特性 并发编程有三个至关重要的特性&#xff0c;分别是原子性、有序性和可见性 1.1 原子性 所谓原子性是指在一次的操作或者多次操作中&#xff0c;要么所有的操作全部都得到了执行并 且不会受到任何因素的干扰而中断&#xff0c;要么所有的操作都不执行…

算法中的移动窗帘——C++滑动窗口算法详解

1. 滑动窗口简介 滑动窗口是一种在算法中常用的技巧&#xff0c;主要用来处理具有连续性的子数组或子序列问题。通过滑动窗口&#xff0c;可以在一维数组或字符串上维护一个固定或可变长度的窗口&#xff0c;逐步移动窗口&#xff0c;避免重复计算&#xff0c;从而提升效率。常…