join 是怎样工作的

Fork/Join工作原理解析 一、如何高效利用CPU 在介绍Fork/Join框架之前,思考一个问题,在多线程的情况下,如何才能高效的利用CPU,以常用的线程池为例,线程池的线程数设置多少合适? 通常调整线程池中的线程数量其核心目的就是为了能够充分的利用CPU和内存资源,从而最大限度的提高程序的性能。在实际工作中,我们可能会根据任务类型的不同而采取不同的策略。按照一个任务执行过程中使用CPU的时间和任务总耗时的比例,可以将任务分为两种:CPU密集型任务和IO密集型任务 1.1 任务类型 1.1.1 CPU密集型任务 CPU密集型任务也称 阅读详情

目录

数据准备

Index Nested-Loop Join(NLJ)(扫描被驱动表用到索引)

Block Nested-Loop Join(BNL)(扫描被驱动表未用到索引)

假设 join bufer 足够大

实际中 join buffer 是有限制的

如何选择驱动表

走索引的情况

不走索引的情况

文中“小表”的含义

举例1

举例2

举例3

结论

Multi-Range Read(MRR)

Batched Key Access(BKA)

BNL 算法的性能问题


数据准备

CREATE TABLE `t2` (
  `id` int(11) NOT NULL,
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_a` (`a`)
) ENGINE=InnoDB;

在表 t2 中插入数据 1000 条数据
for(int i=1; i<=1000; i++) {
    insert into t2(id, a, b) values(i, i, i);
}

创建表 t1,并插入 100 条记录
create table t1 like t2;
insert into t1 (select * from t2 where id<=100);

说明:如果直接使用 join 语句,MySQL 优化器可能会选择表 t1 或 t2 作为驱动表,这样会影响我们分析 SQL 语句的执行过程。所以,为了便于分析执行过程中的性能问题,我改用 straight_join 让 MySQL 使用固定的连接方式执行查询,这样优化器只会按照我们指定的方式去 join。

Index Nested-Loop Join(NLJ)(扫描被驱动表用到索引)

执行sql

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

由于使用了 straight_join 表 t1 是驱动表,t2是被驱动表

通过 explain 查看执行计划

分析执行计划

对 t1 表做了全表扫描,扫描行数为 100 行

扫描 t2 表是时,使用到了索引“index_a”,执行计划中的 ref 显示为 “lp.t1.a” 说明,扫描 t2 表二级索引使用的值是 t1 表的字段 a 的值。

通过分析执行计划,可得出该语句的执行流程如下。

  1. 从表 t1 中读入一行数据 R;
  2. 从数据行 R 中,取出 a 字段到表 t2 里去查找符合条件的记录(扫描 二级索引获取主键id,然后回表);
  3. 将步骤2中的记录跟 R 组成一行,作为结果集的一部分;
  4. 重复执行步骤 1 到 3,直到表 t1 的末尾循环结束。

由于扫描被驱动表的时候,使用了被驱动表的索引,所以我们称之为“Index Nested-Loop Join” 简称“NLJ”

我们再来看一下,这个 sql 语句的执行,一共扫描了多少行,

对表 t1 做了全表扫描,也就是扫了 100 行。

在对表 t2 进行扫描的时候由于走了索引 t2.a 所以也扫描了 100行(回表次数为 100 次)。那么总的扫描行数是 200 行。

Block Nested-Loop Join(BNL)(扫描被驱动表未用到索引)

假设 join bufer 足够大

执行sql

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

由于使用了 straight_join 表 t1 是驱动表,t2是被驱动表

explain 查看执行计划

分析执行计划

对表 t1 做了全表扫描,扫描行数为 100 行

对表 t2 做了全表扫描,Extra 内容包含“Using join buffer (Block Nested Loop)”,说明使用了 join buffer

通过分析执行计划,可得出该语句的执行流程如下。

  1. 对表 t1 进行全表扫描,并把数据放入 join_buffer(由于我们这个语句中写的是 select *,因此是把整个表 t1 放入了内存)
  2. 扫描表 t2,把表 t2 中的每一行取出来,跟 join_buffer 中的数据做对比,满足 join 条件的,作为结果集的一部分响应客户端。

我们再来看一下,这个 sql 语句的执行,一共扫描了多少行,

  • 对表 t1 做了全表扫描,扫描行数 100 行。
  • 对表 t1 做了全表扫描,扫描行数 1000 行。
  • 那么总的扫描行数是 1100 行。

实际中 join buffer 是有限制的

如果驱动表 t1 是个大表,join buffer 放不下,t1 表的数据怎么办。

mysql的处理方法也是简单粗暴,如果放不下,就是将驱动表的数据分段放。执行流程如下

  1. 扫描表 t1,将数据让如 join buffer 中,等放到第 88 行 join buffer man了,就继续执行第2步
  2. 扫描表 t2,把表 t2 中的每一行取出来,跟 join_buffer 中的数据做对比,满足 join 条件的,作为结果集的一部分响应客户端。
  3. 清空 join buffer
  4. 继续扫描 t1,将数据放入 join buffer中,继续执行第2步,直到表 t1 的数据扫描完为止。

我们看下,join buffer 不够大时,一共扫描了多少行。这里我们假设分 t1 表的数据分三次放入 join buffer

  • 对表 t1 做了全表扫描,扫描行数 100 行。
  • 表 t1 的数据分三次放入join buffer,每次都对表 t2 做了全表扫描,扫描行数就是 3*1000=3000
  • 那么总的扫描行数是 3100 行。

如何选择驱动表

走索引的情况

假设被驱动表的行数是 M。每次在被驱动表查一行数据,要先搜索索引 index_a,再搜索主键索引。每次搜索一棵树近似复杂度是以 2 为底的 M 的对数,记为 log2M,所以在被驱动表上查一行的时间复杂度是 2*log2M(乘以2是因为搜索一次就要回表一次)。

假设驱动表的行数是 N,执行过程就要扫描驱动表 N 行,然后对于每一行,到被驱动表上匹配一次。

因此整个执行过程,近似复杂度是 N + N*2*log2M。显然,N 对扫描行数的影响更大,因此应该让小表来做驱动表。

不走索引的情况

假设驱动表的行数是 N,被驱动表的行数是 M, join buffer 每次执行放入 H 条记录。

那么扫描行数就是N + (N/H + 1) * M,驱动表行数越少,扫描的总行数就越少,因此应该让小表来做驱动表。

文中“小表”的含义

小表不一定表的总记录数少的表

举例1

比如 select * from t1 join t2 on (t1.b=t2.b) where t2.id <= 10(这里举的例子是没有被驱动表未使用索引)

会将 t2 作为驱动表,因为只需要将 t2 中的 10 条数据放入 join buffer 中即可,如果将 t1 作为驱动表 就需要放入 100 条数据到 join buffer中。

举例2

select t1.b, t2.* from t1 straight_join t2 on (t1.b=t2.b) where t2.id<=100;(与 select t1.b,t2.* from t1 join t2 on (t1.b=t2.b) where t2.id<=100 等价)

select t1.b, t2.* from t2 straight_join t1 on (t1.b=t2.b) where t2.id<=100;

表 t1 和 t2 都是只有 100 行参加 join。但是,这两条语句每次查询放入 join_buffer 中的数据是不一样的:

  • 表 t1 只查字段 b,因此如果把 t1 放到 join_buffer 中,则 join_buffer 中只需要放入 b 的值;
  • 表 t2 需要查所有的字段,因此如果把表 t2 放到 join_buffer 中的话,就需要放入三个字段 id、a 和 b。

所以应该选择 t1 作为驱动表

举例3

例子2中,以下两条sql是等价的,都是选择 t1 作为驱动表

select t1.b, t2.* from t1 straight_join t2 on (t1.b=t2.b) where t2.id<=100;

select t1.b,t2.* from t1 join t2 on (t1.b=t2.b) where t2.id<=100;

那么与sql A “select t1.b,t2.* from t2 join t1 on (t1.b=t2.b) where t2.id<=100;”是否是等价的呢。

答案是,不等价的,sql A,选择是将 t2 作为驱动表,根据举例2中的说明,需要将 t2 中的三个字段都放入 join buffer 中。所以在做表 join 的时候需要将作为驱动表的表名现在 “join” 的前面

结论

哪张表作为驱动表,不是看哪张表记录少,也不是看哪张表放入 Join buffer 的记录少。

而是看哪张表放入join buffer 中的数据量少,哪张表就是驱动表

Multi-Range Read(MRR)

MRR 的优化思路是,由于大多数情况下数据都是按照主键递增顺序插入得到的,所以可以认为,如果按照主键的递增顺序查询的话,对磁盘的读比较接近顺序读,能够提升读性能。

做实验前,先打开 MRR,mysql默认是关闭 MRR的 。如果需要使用MRR 需设置 set optimizer_switch="mrr_cost_based=off"。

我们来看下使用了MRR后,sql的执行流程,字段 a 是非唯一二级索引

select * from t where a > 100 and a < 200

  1. 根据索引 a,定位到满足条件的记录,将 id 值放入 read_rnd_buffer 中 ;
  2. 将 read_rnd_buffer 中的 id 进行递增排序;
  3. 排序后的 id 数组,依次到主键 id 索引中查记录,并作为结果返回。

这里,read_rnd_buffer 的大小是由 read_rnd_buffer_size 参数控制的。如果步骤 1 中,read_rnd_buffer 放满了,就会先执行完步骤 2 和 3,然后清空 read_rnd_buffer。之后继续找索引 a 的下个记录,并继续循环。

MRR 能够提升性能的核心在于,这条查询语句在索引 a 上做的是一个范围查询(也就是说,这是一个多值查询),可以得到足够多的主键 id。这样通过排序以后,再去主键索引查数据,才能体现出“顺序性”的优势。

我们看下sql的执行计划

可以看到 Extra 字段出现了“Using MRR”

Batched Key Access(BKA)

BKA 算法是对 NLJ 算法的优化,其实就是在 NLJ 的基础上使用了 MRR

我们看sql select * from t1 straight_join t2 on (t1.a=t2.a) 的执行过程

  1. 对表t1进行全表扫描,并将数据放入 Join buffer 中
  2. 对 join buffer 中的数据,根据主键 id 进行排序
  3. 使用 t1.a 字段的值去,扫描 t2.a 二级索引(这里可以理解为 select * from t2 where t2.a in (1,2,3,4,5······)),并使用 MRR 优化
  4. 将符合条件的记录发送给客户端

BNL 算法的性能问题

  • 将被驱动表数据放入临时表中,给临时表创建索引
  • 业务自行处理

一图胜千言

MySQL Join 工作原理与性能优化全解析 数据库Join操作是SQL查询的核心技术,通过关联多表数据实现复杂查询。其底层实现主要采用嵌套循环、哈希连接等算法,其中索引优化和驱动表选择是关键性能因素。在MySQL 8.0+中引入的Hash Join算法,大幅提升了等值连接效率。工程实践中,通过EXPLAIN分析执行计划、合理配置join_buffer_size、避免类型不匹配等优化手段,可使Join性能提升数倍。典型应用场景包括电商订单关联查询、社交网络关系分析等,其中索引设计要遵循'小表驱动大表'原则,并为被驱动表Join字段建立合适索引。 阅读详情

相关推荐

Java并发之美:Fork/Join框架与工作窃取算法

Fork/Join框架是Java 7引入的一种用于并行计算的框架,它基于工作窃取算法(Work Stealing)。工作窃取算法是一种调度策略,允许工作线程在完成自身任务后,从其他线程的队列中“窃取”任务来执行。这种策略可以有效避免线程空闲,提高CPU的利用率。在Fork/Join框架中,任务被分解为多个子任务,这些子任务可以并行执行。当一个线程完成自己的任务后,它会尝试从其他线程的任务队列中窃取任务来执行。这种机制使得任务能够动态地分配到各个线程中,从而实现高效的并行计算。

2501_90323865的博客 562

mysql 5.5 join排序_MySQL的JOIN(五):JOIN优化实践之排序

这篇博文讲述如何优化JOIN查询带有排序的情况。大致分为对连接属性排序和对非连接属性排序两种情况。插入测试数据。CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT,type INT);SELECT COUNT(*) FROM t1;+----------+| COUNT(*) |+----------+| 10000 |+----------...

weixin_42403100的博客 531

RML2016.10a (CNN、LSTM、CNN-LSTM、CNN-LSTM-Multi-head-attention, TensorFlow)

RML2016.10a (CNN、LSTM、CNN-LSTM、CNN-LSTM-Multi-head-attention, TensorFlow) 包含所有数据和代码,支持一键运行(jupyter)

qq_40999533的博客 1485

深度整理总结MySQL——Join的工作原理

这篇文章是对知识内容进行整合加上自己的理解,文章内容来自于各种文章,书籍,工作号内容.下面的内容我会列举是在哪里看的,然后写一下我自己的理解和总结.如果感觉不清楚的,可以去看一下原创.MySQL 数据库中支持 JOIN 连接的算法有 Nested Loop Join 和 Hash Join 两种,前者通常用于 OLTP 业务,后者用于 OLAP 业务。在 OLTP 可以写 JOIN,优化器会自动选择最优的执行计划。

左灯右行的爱情 1475

lvgl 实现表格table,并且固定表头

嵌入式实现table,lvgl 实现原生表格table组件

m0_61704502的博客 1273

【前端HTML生成条形码——MQ】

本文将展示前端HTML如何根据用户输入内容生成对应的条形码,附HTML和JS代码。代码如下(示例):

weixin_42588676的博客 1957

MySQL join是怎么工作的

首先准备t1 t2表可以看到,这两个表都有一个主键索引 id 和一个索引 a,字段 b 上无索引。存储过程 idata() 往表 t2 里插入了 1000 行数据,在表 t1 里插入的是 100 行数据。

loopnz的博客 152

工作流jbpm中join与fork用法

工作流jbpm中join与fork用法 关键字:   jbpm     join是和fork一起使用的,主要是从fork开始会变成两个分枝,同时并行两个流程,最后两个流程在join汇合,才能走入下面的节点task。 业务罗辑图1 (在附件中,总是图片上传后,在另一台

坚持,才是力量.老实做人,踏实做事. 4463

FFT中的精度博弈:定点数缩放、块浮点与溢出处理在Vivado IP核中的实践权衡

本文探讨了在Vivado FFT IP核中实现高精度快速傅里叶变换的关键技术,包括定点数缩放策略、块浮点模式及溢出处理机制。通过分析不同精度控制方案的资源开销与性能权衡,为工程师在医疗成像、雷达信号处理等高动态范围应用中优化FFT实现提供实践指导。

melon的博客 1098

智能车实战——极速越野组GPS与惯导融合定位导航全解析

本文深入解析了智能车极速越野组中GPS与惯导模块的融合定位导航技术。通过对比GPS的全局定位与惯导的高频姿态感知,详细阐述了如何利用MM32SPN27主控,将两者数据融合以实现精准、实时的车辆控制。内容涵盖硬件选型、软件数据处理、PID控制及系统调试等实战环节,为智能车开发者提供了一套完整的解决方案。

pz8901234的博客 1204

在工作中彻底理解mysql inner join和left join的区别

如何真正理解inner join 和 left join 的区别,通俗解释带你理解,在工作中实际使用带你去理解。

qq_41046474的博客 2270

多模型对抗性审查:用AI辩论提升技术文档质量

在软件工程领域,技术文档是团队协作和项目成功的关键基础。传统的文档评审依赖人工经验,存在盲区和效率瓶颈。通过引入多模型对抗性审查机制,不同大语言模型从各自视角对文档进行批判性分析,模拟现实中的专家评审过程。这种技术方法的核心价值在于利用模型间的思维差异,自动发现单个视角容易忽略的边界情况、逻辑漏洞和隐含假设。在应用场景上,特别适合产品需求文档和技术规格书这类需要多角度审视的复杂文档。adversarial-spec工具通过Claude协调多模型辩论,实现从概念到实现的完整文档质量保障,显著提升文档的完整性和

weixin_42531579的博客 379

join语句的工作机制

join语句的工作机制一、Index Nested-Loop Join二、Simple Nested-Loop Join三、BlockNested-Loop Join四、是否使用join和选择哪个表做驱动表的解答 先创建两个表,都有一个主键索引 id 和一个索引 a ,字段 b 上无索引。存储过程 idata() 往表 t2里插入了 1000 行数据,在表 t1 里插入的是 100 行数据。 接下来我们要探讨两个问题: 1.使用joing有什么问题 2.有两个大小不同的表做 join ,应该用哪个表做驱动表

wokoone的博客 318

C# 上位机内存泄漏排查指南:3 个工具 + 4 个技巧,从 GC 频繁到稳定运行 720 小时

C# 上位机内存泄漏排查的核心是「先定位泄漏存在,再追溯引用根源,最后修复验证通过「Visual Studio 诊断工具(快速定位)+ dotMemory(深度分析)+ PerfView(生产监控)」三个工具的配合,结合「复现场景、排查静态成员+事件、检查非托管资源、GC 日志验证」四个技巧,可高效解决绝大多数泄漏问题。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 739

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

hive join 的工作机制

hive join 的工作机制

qq_43192537的博客 440

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

MySQL join 是如何工作的?join 优化?

● 如果是大表 join 小表的话,时间复杂度是 n * logm。● 如果是小表 join 大表的话,时间复杂度是 m * logn。MySQL join 是如何工作的?● 显然小表 join 大表的时候,时间复杂度较小。● 假设小表有 m 条记录,大表有 n 条记录。● join 的 字段,可以加索引,来优化。● 建议 join 拆分成多次单表的操作。驱动表 join 被驱动表。

拒绝八股,追寻本质。 151

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Excel 工作表做 JOIN

【问题】 I’m working on combining two excel worksheets. Before I start, I’d like to mention that I also have mysql workbench, so I’m open to working on this issue in either sql or vba (I should learn both). I’m working with .bed files, which are lists of geno

Raqsoft 806

09-jbpm工作流之"分支聚合Join-Fork"

工作流的分支与聚合:       这是多个分支并行(同时)执行,并且所有的分支Execution都到Join活动聚合后才向后执行; 1.工作流图:test.png 工作流配置文件:test.jpdl.xml:

全力付出 2829

你知道工作窃取算法吗?你知道 Java 中的 fork/join 框架吗?

本文我们将讨论 Java 中工作窃取的概念。在 JDK7 中引入了工作窃取,旨在减少多线程应用程序中的竞争。这是使用 fork/join 框架完成的。在 fork/join 框架中,问题或任务被递归分解为子任务。然后单独解决子任务,子结果组合成结果。分解任务在线程池提供的工作线程的帮助下解决。每个工作线程都有它负责的子任务。这些存储在双端队列(deques)中。每个工作线程通过不断从 d...

Shockang的博客 783

MapReduce高级编程(计数器、mysql读写、join、工作流)

MapReduce高级编程(计数器、mysql读写、join、工作流)

qq_46439199的博客 1633

17、ForkJoinPool之工作线程的启动与任务的fork以及join

一、工作线程的启动 ForkJoinPool创建的工作线程类型为ForkJoinWorkerThread,下面是它的run方法 1.1 启动扫描任务 public void run() { if (workQueue.array == null) { // only run once Throwable exception = null; try { ...

zhen_hong的博客 1535

mysql join工作原理_MySQL JOIN原理

JOIN原理join 主要有Nested Loop、Hash Join、Merge Join这三种方式。以下只讨论Nested Loop;Nested Loop意为嵌套循环,类似于forNested Loop又可以细分为三种连接方式:Simple Nested-Loop Join(简单嵌套循环)、Index Nested-Loop Join(索引)、Block Nested-Loop Join(块...

weixin_39555715的博客 751

join的工作原理和UDF的定义

深入join的过程 虽然我们在Spark篇的SparkSQL入门中也提到了一些join的东西,但是还是不够详细。这里我们将在Hive中执行join操作,看看在执行计划中究竟是怎么样的。 现在我们有这么一个SQL语句,里面是对两个表进行join: select a.empno,a.ename,a.deptno,b.dname from emp a join dept b on a.deptno...

heima201907的博客 270

深入探讨 Fork/Join 框架的底层工作原理

深入探讨 Fork/Join 框架的底层工作原理

daweias的博客 505
上一篇: group by 是怎样工作的
下一篇: 事务是怎样工作的
三易的博客
博客等级 码龄15年 42粉丝 89原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值