mysql--join

news2024/12/28 20:56:57

Join

两个表都有一个主键索引 id 和一个索引 a,字段 b 上无索引,表 t2 里插入了 1000 行数据,在表 t1 里插入的是 100 行数据

CREATE TABLE `t2` (
  `id` int(11) NOT NULL,
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `a` (`a`)
) ENGINE=InnoDB;
 
drop procedure idata;
delimiter ;;
create procedure idata()
begin
  declare i int;
  set i=1;
  while(i<=1000)do
    insert into t2 values(i, i, i);
    set i=i+1;
  end while;
end;;
delimiter ;
call idata();
 
create table t1 like t2;
insert into t1 (select * from t2 where id<=100)

straight_join 让 MySQL 使用固定的连接方式执行查询,前表驱动后表

Index Nested-Loop Join

前提是“可以使用被驱动表的索引”

-- t1 是驱动表,t2 是被驱动表
-- 被驱动表 t2 的字段 a 上有索引,join 过程用上了这个索引
select * from t1 straight_join t2 on (t1.a=t2.a);

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

先遍历表 t1,然后根据从表 t1 中取出的每行数据中的 a 值,去表 t2 中查找满足条件的记录。与嵌套查询类似,并且可以用上被驱动表的索引,所以我们称之为“Index Nested-Loop Join”,简称 NLJ。
Index Nested-Loop Join 算法的执行流程

分析:

对驱动表 t1 做了全表扫描,这个过程需要扫描 100 行;
而对于每一行 R,根据 a 字段去表 t2 查找,走的是树搜索过程。由于我们构造的数据都是一一对应的,因此每次的搜索过程都只扫描一行,也是总共扫描 100 行;
所以,整个执行流程,总扫描行数是 200。

复杂度:

这个 join 语句执行过程中,驱动表是走全表扫描,而被驱动表是走树搜索。
假设被驱动表的行数是 M。每次在被驱动表查一行数据,要先搜索索引 a,再搜索主键索引。每次搜索一棵树近似复杂度是以 2 为底的 M 的对数,记为 log2M,所以在被驱动表上查一行的时间复杂度是 2log2M。
假设驱动表的行数是 N,执行过程就要扫描驱动表 N 行,然后对于每一行,到被驱动表上匹配一次。
因此整个执行过程,近似复杂度是 N + N
2*log2M。N 对扫描行数的影响更大,因此应该让小表来做驱动表。

结论:

使用 join 语句,性能比强行拆成多个单表执行 SQL 语句的性能要好;
如果使用 join 语句的话,需要让小表做驱动表

Block Nested-Loop Join

被驱动表上没有可用的索引

-- 由于表 t2 的字段 b 上没有索引,每次到 t2 去匹配的时候,就要做一次全表扫描。
select * from t1 straight_join t2 on (t1.a=t2.b);

执行流程:

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

Block_Nested-Loop_Join算法的执行流程

复杂度:

假设小表的行数是 N,大表的行数是 M
两个表都做一次全表扫描,所以总的扫描行数是 M+N;
内存中的判断次数是 M*N。
调换这两个算式中的 M 和 N 没差别,因此这时候选择大表还是小表做驱动表,执行耗时是一样的。

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

  • 如果 join_buffer 放不下表 t1 的所有数据话,策略很简单,就是分段放

执行过程:

1、扫描表 t1,顺序读取数据行放入 join_buffer 中,放完第 88 行 join_buffer 满了,继续第 2 步;
2、扫描表 t2,把 t2 中的每一行取出来,跟 join_buffer 中的数据做对比,满足 join 条件的,作为结果集的一部分返回;
3、清空 join_buffer;
4、继续扫描表 t1,顺序读取最后的 12 行数据放入 join_buffer 中,继续执行第 2 步。
Block_Nested-Loop_Join两段

分析:

假设,驱动表的数据行数是 N,需要分 K 段才能完成算法流程,被驱动表的数据行数是 M。
K 不是常数,N 越大 K 就会越大,因此把 K 表示为λ*N,显然λ的取值范围是 (0,1)。

复杂度:

扫描行数是 N+λNM;
内存判断 N*M 次。
内存判断次数是不受选择哪个表作为驱动表影响的。而考虑到扫描行数,在 M 和 N 大小确定的情况下,N 小一些,整个算式的结果会更小。

使用 join 语句

1、如果可以使用 Index Nested-Loop Join 算法,也就是说可以用上被驱动表上的索引,其实是没问题的;
2、如果使用 Block Nested-Loop Join 算法,扫描行数就会过多。尤其是在大表上的 join 操作,这样可能要扫描被驱动表很多次,会占用大量的系统资源。所以这种 join 尽量不要用。

join 使用“小表”做驱动表

在决定哪个表做驱动表的时候,应该是两个表按照各自的条件过滤,过滤完成之后,计算参与 join 的各个字段的总数据量,数据量小的那个表,就是“小表”,应该作为驱动表。

join优化

Multi-Range Read 优化 (MRR)

优化的主要目的是尽量使用顺序读盘

回表是指,InnoDB 在普通索引 a 上查到主键 id 的值后,再根据一个个主键 id 的值到主键索引上去查整行数据的过程。
主键索引是一棵 B+ 树,在这棵树上,每次只能根据一个主键 id 查到一行数据。回表是一行行搜索主键索引的


如果随着 a 的值递增顺序查询的话,id 的值就变成随机的,那么就会出现随机访问,性能相对较差。
按照主键的递增顺序查询的话,对磁盘的读比较接近顺序读,能够提升读性能
MRR 优化的设计思路:

1、根据索引 a,定位到满足条件的记录,将 id 值放入 read_rnd_buffer 中 ;
2、将 read_rnd_buffer 中的 id 进行递增排序;
3、排序后的 id 数组,依次到主键 id 索引中查记录,并作为结果返回。
MRR执行流程
read_rnd_buffer 的大小是由 read_rnd_buffer_size 参数控制的。如果步骤 1 中,read_rnd_buffer 放满了,就会先执行完步骤 2 和 3,然后清空 read_rnd_buffer。之后继续找索引 a 的下个记录,并继续循环。

想要稳定地使用 MRR 优化的话,需要设置set optimizer_switch=“mrr_cost_based=off”。(官方文档的说法,是现在的优化器策略,判断消耗的时候,会更倾向于不使用 MRR,把 mrr_cost_based 设置为 off,就是固定使用 MRR 了。)

Batched Key Access

BKA 算法,其实就是对 NLJ 算法的优化

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

  • 要使用 BKA 优化算法的话,需要在执行 SQL 语句之前,先设置
-- 前两个参数的作用是要启用 MRR。BKA 算法的优化要依赖于 MRR。
set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';

BNL 算法的性能问题

Block Nested-Loop Join(BNL) 算法对系统的影响

1、可能会多次扫描被驱动表,占用磁盘 IO 资源;
2、判断 join 条件需要执行 M*N 次对比(M、N 分别是两张表的行数),如果是大表就会占用非常多的 CPU 资源;
3、可能会导致 Buffer Pool 的热数据被淘汰,影响内存命中率。

  • 影响 Buffer Pool

由于 InnoDB 对 Bufffer Pool 的 LRU 算法做了优化,即:第一次从磁盘读入内存的数据页,会先放在 old 区域。如果 1 秒之后这个数据页不再被访问了,就不会被移动到 LRU 链表头部,这样对 Buffer Pool 的命中率影响就不大。


如果一个使用 BNL 算法的 join 语句,多次扫描一个冷表,而且这个语句执行时间超过 1 秒,就会在再次扫描冷表的时候,把冷表的数据页移到 LRU 链表头部。


这种情况对应的,是冷表的数据量小于整个 Buffer Pool 的 3/8,能够完全放入 old 区域的情况。


如果这个冷表很大,就会出现另外一种情况:业务正常访问的数据页,没有机会进入 young 区域。


由于优化机制的存在,一个正常访问的数据页,要进入 young 区域,需要隔 1 秒后再次被访问到。但是,由于 join 语句在循环读磁盘和淘汰内存页,进入 old 区域的数据页,很可能在 1 秒之内就被淘汰了。这样,就会导致这个 MySQL 实例的 Buffer Pool 在这段时间内,young 区域的数据页没有被合理地淘汰。


也就是说,这两种情况都会影响 Buffer Pool 的正常运作。

BNL 优化

1、BNL 转 BKA

1、一些情况下,我们可以直接在被驱动表上建索引,这时就可以直接转成 BKA 算法了
2、不适合在被驱动表上建索引的情况

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

在表 t2 中插入了 100 万行数据,但是经过 where 条件过滤后,需要参与 join 的只有 2000 行数据。如果这条语句同时是一个低频的 SQL 语句,那么再为这个语句在表 t2 的字段 b 上创建一个索引就很浪费了。

  • 这时候,我们考虑使用临时表
  -- 把表 t2 中满足条件的数据放在临时表 tmp_t 中;
  -- 为了让 join 使用 BKA 算法,给临时表 tmp_t 的字段 b 加上索引;
   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;
  -- 让表 t1 和 tmp_t 做 join 操作。
  select * from t1 join temp_t on (t1.b=temp_t.b);

  • 业务端自己实现在

1、select * from t1;取得表 t1 的全部 1000 行数据,在业务端存入一个 hash 结构
2、select * from t2 where b>=1 and b<=2000; 获取表 t2 中满足条件的 2000 行数据。
3、把这 2000 行数据,一行一行地取到业务端,到 hash 结构的数据表中寻找匹配的数据。满足匹配的条件的这行数据,就作为结果集的一行。

入一个 hash 结构

2、select * from t2 where b>=1 and b<=2000; 获取表 t2 中满足条件的 2000 行数据。
3、把这 2000 行数据,一行一行地取到业务端,到 hash 结构的数据表中寻找匹配的数据。满足匹配的条件的这行数据,就作为结果集的一行。

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

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

相关文章

图机器学习入门:基本概念介绍

图机器学习&#xff08;Graph Machine Learning&#xff0c;简称Graph ML&#xff09;是机器学习的一个分支&#xff0c;专注于利用图形结构的数据。在图形结构中&#xff0c;数据以图的形式表示&#xff0c;其中的节点&#xff08;或顶点&#xff09;表示实体&#xff0c;边&a…

【LeetCode算法】28. 找出字符串中第一个匹配项的下标

提示&#xff1a;此文章仅作为本人记录日常学习使用&#xff0c;若有存在错误或者不严谨得地方欢迎指正。 文章目录 一、题目二、思路三、解决方案四、JAVA截取字符串的常用方法4.1 通过subString()截取字符串* 一、题目 给你两个字符串 haystack 和 needle &#xff0c;请你在…

某大型央企主数据项目实战,上线前后深度对比与解析

数据已成为数字化转型的核心&#xff0c;如何将数据转化为战略资产&#xff0c;是当今企业迫切需要解决的问题。其中主数据作为企业的“黄金数据”&#xff0c;是数据资产管理实践方式的重要切入方法之一。某大型央企成功实施了主数据管理项目&#xff0c;实现了从数据孤岛到一…

寻找最大价值的矿堆 - 矩阵

系列文章目录 文章目录 系列文章目录前言一、题目描述二、输入描述三、输出描述四、Java代码五、测试用例 前言 本人最近再练习算法&#xff0c;所以会发布一些解题思路&#xff0c;希望大家多指教 一、题目描述 给你一个由’0’(空地)、‘1’(银矿)、‘2’(金矿)组成的地图…

揭秘自行车对人体的神奇力量

想象一下&#xff0c;如果有一种魔法&#xff0c;能让你每施展一次就多一个小时的寿命&#xff0c;你会心动吗&#xff1f;这种魔法确实存在&#xff0c;它就是——骑自行车。没错&#xff0c;你没有听错&#xff0c;根据最新的研究数据&#xff0c;“骑行一小时&#xff0c;长…

vue布局设置——使用 el-drawer 打造个性化 Admin 后台布局设置

在前端开发中&#xff0c;我们常常需要为 admin 后台构建灵活且个性化的布局设置。今天&#xff0c;我要分享的是如何利用 el-drawer 来实现这样一个有趣的功能。 首先&#xff0c;我们来看一下主要的设置参数&#xff1a; 1. theme: 用于定义主题&#xff0c;可以根据需求切换…

政安晨:【Keras机器学习示例演绎】(四十二)—— 使用 KerasNLP 和 tf.distribute 进行数据并行训练

目录 简介 导入 基本批量大小和学习率 计算按比例分配的批量大小和学习率 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras机器学习实战 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在…

电脑文件找回恢复软件分享,3款软件助力数据恢复!

使用电脑时一不小心就删除了重要文件&#xff1f;想找回数据却不知道应该怎么操作&#xff1f;众所周知&#xff0c;电脑的使用已经成为我们工作、学习和生活中不可或缺的工具&#xff0c;但是在操作时误删文件是令很多用户都头疼的问题。 怎么恢复电脑里被删除的文件呢&#…

LabVIEW天然气压缩因子软件设计

LabVIEW天然气压缩因子软件设计 项目背景 天然气作为一种重要的能源&#xff0c;其压缩因子的准确计算对于流量的计量和输送过程的优化具有关键意义。传统的计算方法不仅步骤繁琐&#xff0c;而且难以满足现场快速响应的需求。因此&#xff0c;开发一款既能保证计算精度又便于…

一个开源即时通讯源码

一个开源即时通讯源码 目前已经含服务端、PC、移动端即时通讯解决方案&#xff0c;主要包含以下内容。 服务端简介 不要被客户端迷惑了&#xff0c;真正值钱的是服务端&#xff0c; 服务是采用Java语言开发&#xff0c;基于spring cloud微服务体系开发的一套即时通讯服务端。…

【XR806开发板试用】试用SWD+Jlink调试

XR806开发板&#xff0c;只能使用编写代码&#xff0c;然后通过UART下载&#xff0c;没法在线debug&#xff0c; 效率会差很多&#xff0c;官方没有提供这一方面的资料。 先查CPU&#xff0c; 官方介绍是arm-china的MC1&#xff0c;通过armv8 Architecture refenence manual资料…

linux打包流程

因为linux有俩个python版本&#xff0c;我们需要切换到python3这个版本&#xff0c;默认是python 2.7 alias pythonpython3 切换到python3 再次执行&#xff1a;python -V 显示出python的版本了&#xff0c;然后查看pip的配置&#xff0c;我们打包里面需要的第三方需要放到pip…

使用IDA自带python patch的一道例题

首先看见就是迷宫 迷宫解出的路径&#xff0c;放在zip的文件可以得到一个硬编码 然后在原程序中&#xff0c;有一处很离谱 这个debugbreak就是IDA分析错误导致的 我们点进去发现里面全是nop 然后我们把我们得到的硬编码放在010里面&#xff0c;再用IDA打开 重新编译看汇编 你…

高性能运营级流媒体服务框架:支持多协议互转 | 开源日报 No.250

ZLMediaKit/ZLMediaKit Stars: 12.6k License: NOASSERTION ZLMediaKit 是一个基于 C11 的高性能运营级流媒体服务框架。 使用 C11 开发&#xff0c;避免裸指针&#xff0c;代码稳定可靠&#xff0c;性能优越。支持多种协议 (RTSP/RTMP/HLS/HTTP-FLV/WebSocket-FLV/GB28181 等…

JavaEE之线程(3)_线程的开始、中断、等待、休眠线程、线程的状态

前言 在本栏的上一节&#xff08;https://blog.csdn.net/2301_80653026/article/details/138500558&#xff09;&#xff0c;我们重点讲解了五种不同的创建线程的方式&#xff0c;我们还介绍了Tread类的常见构造方法和常见属性&#xff0c;在这一节中我们将会继续介绍Tread类。…

5/11后面部分:+顺序排序+元素交换+计算每门课程的各种成绩+存放规律的数据 注意:一味的复制肯定要出问题,第2个的最后一部分有修改,注意观察

目录 第一个已经输出过一次&#xff1a; 第二个: 编程实现&#xff1a;程序功能是用起泡法对数组中n个元素按从大到小的顺序进行排序。 ​编辑的确出现了一些问题哦&#xff1a; ​编辑目前是可以运行&#xff0c;但AI不给我们通过&#xff1a; 最后还是我的代码获胜&#x…

苹果电脑怎么安装crossover 如何在Mac系统中安装CrossOver CrossOver Mac软件安装说明

很多Mac的新用户在使用电脑的过程中&#xff0c;常常会遇到很多应用软件不兼容的情况。加上自己以前一直都是用Windows系统&#xff0c;总觉得Mac系统用得很难上手。 其实&#xff0c;用户可以在Mac上安装CrossOver&#xff0c;它支持用户在Mac上运行Windows软件&#xff0c;例…

C++牛客小白月赛题目分享(1)生不逢七,交换数字,幻兽帕鲁

目录 1.前言 2.三道题目 1.生不逢七 1.题目描述 2.输入描述: 3.输出描述: 4.示例&#xff1a; 5.题解&#xff1a; 2.交换数字 1.题目描述&#xff1a; 2.输入描述&#xff1a; ​编辑 3.输出描述&#xff1a; 4.示例&#xff1a; 5.题解&#xff1a; 3.幻兽帕…

开源基金会AtomGit教程

大家伙&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 注册 点击链接进入官网&#xff0c;先注册&#xff0c;然后登录&#xff1a;https://atomgit.com/ 编辑个人资料&#xff0c;修改一下自己的信息&#xff0c;用户名后面提交代码的时候要…

frida反检测

一、frida介绍 简单来说&#xff0c;Frida是一个动态代码插桩框架。这意味着它能够在程序运行的时候&#xff0c;不改变原有程序代码的情况下&#xff0c;让你往里面添加自己的代码片段。这听起来可能有点像魔法&#xff0c;但实际上&#xff0c;它是通过一种叫做“HOOK”的技术…