MySQL表连接查询算法

  1.  前言

MySQL属于关系型数据库,我们建的表大多也都存在业务上的关联关系,同时我们又不可能将所有的数据都冗余一份,这不符合数据库的设计范式。因此,当我们需要把多张表的数据融合在一起的时候,就需要使用到「多表连接查询」。

多表连接查询虽然用的很爽,但是常常会带来性能问题。大家可以回忆一下自己遇到的慢SQL,大多数都是多表联查导致的。有的DBA甚至会要求严格限制连接查询中表的数量,理论上来说,连接表的数量越多,效率越低。表连接最坏的情况,就是「笛卡尔积」,它没有任何限制条件,结果集中包含一张表中所有的记录与其它表所有的记录相互匹配的组合。两张各一万条记录的表,产生的笛卡尔积就有一亿条记录,如果是三张表结果更是不敢想象。

大家也不用被笛卡尔积吓到,它只是最坏的结果而已,只要使用得当,连接查询的效率也可以很高。

2. 连接查询的过程
在介绍具体的连接算法前,我们先来熟悉一下MySQL连接查询的过程。现有两张表t1、t2结构如下:

CREATE TABLE t1(
    `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `a` INT NOT NULL DEFAULT 0
)ENGINE=InnoDB AUTO_INCREMENT=1;

CREATE TABLE t2(
    `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `b` INT NOT NULL DEFAULT 0
)ENGINE=InnoDB AUTO_INCREMENT=1;


使用存储过程向两张表格插入一些记录,然后执行下面这个SQL:

SELECT * FROM t1 INNER JOIN t2 ON t2.b=t1.id WHERE t1.id<10;

对于左外连接和右外连接,表的连接顺序是固定的,MySQL优化器能帮我们优化的点比较少,所以我们以内连接为主进行分析。对于内连接查询,表的连接顺序是可以互换的,因此MySQL优化器可以分别计算以t1为驱动表和以t2为驱动表的查询成本各是多少,然后选出执行成本更低的方案,也就是最终的执行计划。

具体执行成本的计算不在本文的讨论范围内,先跳过。

对于这个查询,我们指定了两个过滤条件:

t1.id < 10
t2.b = t1.id < 10


1、确定驱动表

MySQL首先需要从t1和t2中确定谁是驱动表。对于内连接,表的连接顺序是可以互换的,意味着t1和t2都可以作为驱动表,就看谁的执行成本更低了。MySQL会根据表/索引的统计数据或访问表中少量的数据来评估各自的执行成本,细节我们就先跳过。

2、驱动表获取到的每一条记录去被驱动表中进行匹配。

假设t1是驱动表,查询过程是这样的:

获取t1表中第一条id<10的记录。
拿着这个记录的id去查询t2表,条件是t2.b=t1.id,由于t2.b没有索引,所以只能对t2做全表扫描。符合条件的记录返回,不符合条件的记录丢弃。
获取t1表中第二条id<10的记录,重复前面的流程,直到t1表中没有id<10的记录了。
t1表作为驱动表,只需要访问一次,且id是主键,因此t1.id<10可以使用range访问方法,效率还是很高的。问题在于对t2表的访问,由于t2.b没有索引,因此对于t1表中符合条件的每一条记录,都需要全表扫描一次t2表,这个开销是非常大的。

假设t2是驱动表,查询过程是这样的:

对t2进行全表扫描,取出第一条t2.b<10的记录。
拿着该记录列b的值,去查询t1表,条件是t1.id=t2.b。因为t1.id是主键,所以可以使用const访问方法,效率是很高的。
取出第二条t2.b<10的记录,重复前面的流程,直到t1表中没有b<10的记录了。
t2表作为驱动表,只需要对t2全表扫描一次,但是需要对t1表的主键做等值匹配多次,匹配的次数取决于t2表中b<10的记录数量。

综上所述,t1表作为驱动表最大的开销是需要对t2表进行多次全表扫描,全表扫描的次数取决于t1.id<10的记录数量。t2表作为驱动表需要对t2全表扫描一次,但是需要对t1表主键等值匹配多次,匹配的次数取决于t2.b<10的记录数量。除非t1.id<10的记录足够的少,否则MySQL会跟偏向于使用t2作为驱动表,因为ALL比const访问效率要低的多。

笔者实测,对于t1.id<5MySQL认为t1表过滤出的记录足够少,使用t1作为驱动表的成本更低。


而对于t1.id<100MySQL担心会对t2执行太多次全表扫描,因此更偏向于使用t2作为驱动表。


Using join buffer (Block Nested Loop)代表MySQL使用了基于块的嵌套循环连接算法,减少全表扫描的次数。

3. 连接算法
MySQL表连接查询,支持多种连接算法。

3.1 Simple Nested Loop Join(简单嵌套循环连接)
简单嵌套循环连接,最简单也最笨拙的一种算法。从t1表读取的每一条记录,都需要扫描t2表的所有记录进行匹配。

这个过程就像是一个嵌套的循环,连接的表的数量就是循环嵌套的层数,当表中数据量较大,或者连接的表的数量过多时,这种算法的效率是极低的。

对于t1表获取到的每一条记录,都会立马去t2表进行匹配查询,而不会等t1表的记录检索完成了再去查询t2。说白了,MySQL并不会使用额外的空间来保存t1表记录的检索结果。

3.2 Index Nested Loop Join (索引嵌套循环连接)
第一种算法,t1表过滤出多少条记录,就需要对t2表进行多少次全表扫描。对于t1表的每一条记录,其实都是一次针对于t2的单表查询,如果我们能优化每一次对t2的单表查询效率,也就能优化整个连接查询的效率了。

如何优化针对t2的单表查询效率?加索引啊!如果连接查询的过滤条件是t2表的列b,那么就给列b加索引。如果加的是普通索引,那么访问方法可以从ALL优化为ref,如果加的是唯一索引,更是能直接优化为const。

3.3 Block Nested Loop Join (缓冲区嵌套循环连接)
现在我们已经知道,如果不能通过索引来加速被驱动表的查询效率,MySQL只能对被驱动表执行N次全表扫描了。如果表中的记录很少,碰巧机器的内存又很大,全表扫描尚且可以接受,因为内存可以容纳足够多的索引页,N次全表扫描的开销也只需要将聚簇索引的叶子节点加载到内存中一次,后面都可以命中缓存了。然而现实情况是表中的数据通常会很多,且内存又十分紧张。这种场景下,InnoDB加载完后面的索引页,前面的索引页缓存就不得不淘汰了,第二次全表扫描又得来一遍IO,这个开销就没法接受了。MySQL还能怎么优化呢?

MySQL提出了一个名为Join Buffer的概念,连接缓冲区,在执行连接查询前先申请一块固定大小的内存空间,然后将驱动表中的若干条记录放到Join Buffer里,访问被驱动表时就可以一次性与Join Buffer里的所有记录进行匹配了,这样可以显著减少被驱动表的访问次数。理论上Join Buffer越大越好,最好能大到可以容纳驱动表过滤出的所有记录,这样被驱动表只需要全表扫描一次就好了。这种基于Join Buffer来减少被访问表的访问次数的算法,就是Block Nested Loop Join。

Join Buffer不会存放驱动表的所有列信息,只会存储SELECT查询的列和过滤条件涉及到的列,所以查询时要尽量避免使用SELECT *

4. 总结
        连接查询可以将多张表的数据汇总到一起,使用连接查询时要特别注意性能问题,最差的结果就是笛卡尔积。MySQL支持多种连接查询算法,最简单最笨拙的就是简单嵌套循环连接,驱动表只需要访问一次,被驱动表要访问多次,访问次数取决于驱动表过滤出的记录数量。
连接查询的主要开销,来自于对被驱动表的访问。驱动表过滤出的每一条记录其实都对应着被驱动表的一次单表查询,我们可以通过给被驱动表加索引的方式,来优化对被驱动表的查询效率,进而提高连接查询的效率,这就是基于索引的嵌套循环连接。
如果实在是无法通过索引来提高连接查询的效率,MySQL还可以通过Join Buffer性存储若干条驱动表的记录,访问被驱动表时就可以一次性与这些记录进行匹配了,显著减少访问被驱动表的次数。

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

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

相关文章

CSS伪元素详解

CSS伪元素详解 一、引言 在CSS中&#xff0c;伪元素是一个非常强大的工具&#xff0c;它允许我们为元素的特定部分添加样式&#xff0c;而无需修改HTML结构。这不仅提高了样式的灵活性&#xff0c;还有助于保持代码的整洁和可维护性。本文将深入探讨CSS伪元素的使用方法和一些…

详解tar.gz, tar.xz, tar, gz后缀文件的区别

详解tar.gz, tar.xz,tar, gz后缀文件的区别 tar.gz、tar.xz、tar 和 gz 是常见的文件压缩与归档格式&#xff0c;它们的区别主要在于文件的归档和压缩方式。 1. tar 文件 全称&#xff1a;Tape Archive扩展名&#xff1a;.tar说明&#xff1a;tar 文件本身并没有压缩&#x…

SQL分类中的DDL

DDL&#xff08;Data Definition Language):数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库&#xff0c;表&#xff0c;字段&#xff09;。 一、DDL语句操作数据库 1、查询所有数据库&#xff1a;show databases&#xff1b;&#xff08;一般用大写&#xff…

C语言动态内存开辟

文章目录 malloc函数free函数calloc函数realloc函数二维数组的动态内存开辟 malloc函数 malloc函数包含再#include<stdlib.h>头文件中 void* malloc (size_t size);这个函数向内存申请⼀块连续可⽤的空间&#xff0c;并返回指向这块空间的指针。 如果开辟成功&#xff…

OpenCV-人脸检测

文章目录 一、人脸检测流程二、关键方法三、代码示例四、注意事项 OpenCV是一个开源的计算机视觉和机器学习软件库&#xff0c;它提供了多种人脸检测方法&#xff0c;以下是对OpenCV人脸检测的详细介绍&#xff1a; 一、人脸检测流程 人脸检测是识别图像中人脸位置的过程&…

【Docker系列】Docker查看镜像架构

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

模态与非模态的对话框

本文学习自&#xff1a; 《Qt Creato快速入门》 #include "widget.h" #include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); }1. #include "widget.h" #include "ui_w…

MySQL数据的导入

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…

小白也能学会的预测新模型!ReliefF特征选择+XGBoost回归!

小白也能学会的预测新模型&#xff01;ReliefF特征选择XGBoost回归&#xff01; 目录 小白也能学会的预测新模型&#xff01;ReliefF特征选择XGBoost回归&#xff01;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现ReliefF-XGBoost多变量回归预测 1.excel数据…

linux应用

检查Python程序未运行则重新运行 entity_program定时杀掉进程重新运行 match_program定时检查是否运行&#xff0c;未运行则启动 (注意echo时间时&#xff0c;date和中间要有空格) #!/bin/bash# 检测的Python程序名称 entity_program"entity.py" match_program"…

算法收敛的一些证明方法与案例

证明一个算法收敛通常涉及多个角度&#xff0c;以下是一些常用的方法和示例&#xff1a; 一、方法 1. 数学归纳法 通过数学归纳法证明算法在每一步的输出结果都在收敛范围内。 示例&#xff1a;考虑一个递归算法&#xff0c;假设我们要证明它在每一步中输出的值逐渐接近目标…

有问必答!zabbix“专家坐诊”第259期问答

问题一 Q&#xff1a;现在监控项4万多&#xff0c;调整到多少比较合理 zabbix7.03&#xff1f; A&#xff1a;慢慢往上调&#xff0c;没有标准。 问题二 Q&#xff1a;想问下大家&#xff0c;zabbix的监控项怎么不能自动清除&#xff0c;比如说这次监控是A监控项&#xff0c;下…

[LeetCode] 315. 计算右侧小于当前元素的个数

题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;按要求返回一个新数组 counts 。数组 counts 有该性质&#xff1a; counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。 题目链接&#xff1a; . - 力扣&#xff08;LeetCode&#xff09; 题目主要思路&a…

如何通过构建对应的api服务器使Vue连接到数据库

一、安装数据库驱动 在后端安装 MySQL 数据库驱动&#xff0c;比如在 Node.js 环境中可以使用 mysql2 包来连接 MySQL 数据库。在项目目录下运行以下命令安装&#xff1a; npm install mysql2或者使用 yarn&#xff1a; yarn add mysql2二、创建数据库连接模块 创建一个专门…

Light灯光组件+组件的相关操作+游戏资源的加载

Light灯光组件 Type: Directional:平行光&#xff0c;模仿的是太阳光 Spot:聚光灯 Area:区域光 Color&#xff1a; 颜色值 Mode: RealTime:实时 Mix:混合 Baked:烘焙 Intersity: 光照强度 Indirect Multiplier:光照强度乘数 Shadow Type:影子设置&#xff1a;…

Maven和Gradle的对比

Maven和Gradle都是Java项目构建工具&#xff0c;它们在帮助开发者管理项目依赖、编译、打包等方面发挥着重要作用。 Maven和Gradle的区别 1、语法与配置文件 Maven使用XML作为配置文件&#xff08;如pom.xml&#xff09;的语言&#xff0c;XML结构清晰但相对冗长。Gradle则使…

Java通过RAG构建专属知识问答机器人_超详细

RAG&#xff1a;融合检索与生成的文本精准生成技术 检索增强生成&#xff08;RAG&#xff09;是一种技术&#xff0c;它通过结合检索模型和生成模型来提高文本生成的准确性。具体来说&#xff0c;RAG首先利用检索模型从私有或专有的数据源中搜索相关信息&#xff0c;然后将这些…

CentOS上安装SSL证书教程

在 CentOS 上&#xff0c;apt-get 是不可用的&#xff0c;因为 CentOS 使用的是 yum 或 dnf 包管理器。你可以通过 yum 或 dnf 安装 certbot 和 python3-certbot-nginx。以下是详细的步骤&#xff1a; 1. 启用 EPEL&#xff08;Extra Packages for Enterprise Linux&#xff0…

智能优化算法-水循环优化算法(WCA)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1.内容介绍 水循环优化算法 (Water Cycle Algorithm, WCA) 是一种基于自然界水循环过程的元启发式优化算法&#xff0c;由Shah-Hosseini于2012年提出。WCA通过模拟水滴在河流、湖泊和海洋中的流动过程&#xff0c;以及蒸发…

【load_file读文件】

一、文件操作基础 show 先试试 show variables;发现显示了三百多行的系统变量: 这是数据库的目录&#xff1a; mysql有多种编码方式&#xff0c;有数据库编码、连接时的编码、还有客户端的编码&#xff1a; 这里还有一个日志路径&#xff0c;这个日志是需要手动打开的&#…