mysql join语句的执行流程是怎么样的

join语句是使用十分频繁的sql语句,同样结果的join语句,写法不同会有非常大的性能差距。

select * from t1 straight_join t2 on (t1.a=t2.a);a字段都有索引

  1. TRAIGHT_JOIN语法能指定使用左边的表作为join语句的驱动表,join是让执行器自动选择。以上语句会选择t1作为驱动表。

  2. join语句,mysql内部执行时候会采用2中算法。一个是NLJ(Index Nested-Loop Join)。一个是BNL(Block Nested-Loop Join)

  3. NLJ:在join语句执行过程中,如果可以使用到被驱动表的索引,我们称之为“Index Nested-Loop Join”,简称 NLJ。

  4. 驱动表是走全表扫描,而被驱动表是走树搜索,所以驱动表行数越小越好。扫描行数多,性能影响更大,因此应该让小表来做驱动表。

  5. 如果驱动表有索引,被驱动表没有索引,这种情况下,驱动表全表扫描后,去被驱动表中匹配where语句的条件,在被驱动表找一条数据又是全表扫描。这样整个join扫描行数会内指数级别扩大。这种叫“Simple Nested-Loop Join”算法。

  6. 基于第五点,这种情况太笨重。所以msql没有采用”Simple Nested-Loop Join”算法,而是叫“Block Nested-Loop Join”的算法,简称 BNL。被驱动表没有索引情况下,他的逻辑流程是这样的:

    1. 把表 t1 的数据读入线程内存 join_buffer 中,由于我们这个语句中写的是 select *,因此是把整个表 t1 放入了内存;

    2. 扫描表 t2,把表 t2 中的每一行取出来,跟 join_buffer 中的数据做对比,满足 join 条件的,作为结果集的一部分返回。

    1. explain语句查询出来会有use join buffer (block nested loop)关键字

    2. join语句采用BNL算法,虽然对表 t1(100行) 和 t2(1000行) 都做了一次全表扫描,因此总的扫描行数是1100。由于 join_buffer 是以无序数组的方式组织的,因此对表 t2 中的每一行,都要做 100 次判断,总共需要在内存中做的判断次数是:100*1000=10 万次。对比simple Nested-Loop Join算法他是在内存中做对比计算。能大大提供性能。

    3. join_buffer 的大小是由参数join_buffer_size设定的,默认值是 256k。如果放不下表 t1 的所有数据话,策略很简单,就是分段放。就是放多少先处理多少先作为结果集返回,然后清空join_buffer,继续读取后面的数据。

    4. 所以考虑到join_buffer大小有限,让小表作为驱动表,分段情况下,分段次数少。也应该让小表作为驱动表。

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

    join语句优化:

    1. mysql在join语句时,内部做了一些优化,即:Multi-Range Read 优化 (MRR)。这个优化的主要目的是尽量使用顺序读盘。原理是:mysql的索引数据目录中,都是有序的,我们读入数据后,按主键排下序。这样就极大可能在磁盘是顺序读盘。这引入了read_rnd_buffer ,它的大小是由 read_rnd_buffer_size 参数控制的。

    2. 如果你想要稳定地使用 MRR 优化的话,需要设置set optimizer_switch="mrr_cost_based=off"。(官方文档的说法,是现在的优化器策略,判断消耗的时候,会更倾向于不使用 MRR,把 mrr_cost_based 设置为 off,就是固定使用 MRR 了。)explain语句也会有use MRR关键字

    3. 在使用BNL算法时候,引擎是一行一行读取数据。这样就用不上MRR算法优化,所以采取了BKA (Batched Key Access)算法。他可以一次性从驱动表多读一些数据,这些数据临时放在join_buff中。(之前BNL算法用不上join_buff,就利用了这个空间)。

    4. NBL算法优化后的BKA算法后,执行流程如下:

    5. 如果 join buffer 放不下 P1~P100 的所有数据,就会把这 100 行数据分成多段执行上图的流程。如果要使用 BKA 优化算法的话,你需要在执行 SQL 语句之前,先设置set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';前两个参数的作用是要启用 MRR。这么做的原因是,BKA 算法的优化要依赖于 MRR。

    6. BNL算法数据太大,稍不主机就会极大影响mysql服务性能,导致Buffer Pool命中率变低。大表 join 操作虽然对 IO 有影响,但是在语句执行结束后,对 IO 的影响也就结束了。但是,对 Buffer Pool 的影响就是持续性的,需要依靠后续的查询请求慢慢恢复内存命中率。

    7. BNL 算法对系统的影响主要包括三个方面:

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

      1. BNL 转 BKA算法,在驱动表和被驱动表建索引,如果不方便建索引(数据大,join语句不频繁),可以人工主动使用临时表中转,拆分多个语句转化成BKA算法。
      2. hash join。条件匹配是n x m级别计算,如果 join_buffer 里面维护的不是一个无序数组,而是一个哈希表的话,那么就不是 10 亿次判断,而是 100 万次 hash 查找。mysql不支持哈希 join。并且,MySQL 官方的 roadmap,也是迟迟没有把这个优化排上议程。备注:mysql8.0已经支持

执行流程:

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

mysql join语句的执行流程是怎么样的的更多相关文章

  1. mysql update语句的执行流程是怎样的

    update更新语句流程是怎么样的 update更新语句基本流程也会查询select流程一样,都会走一遍. update涉及更新数据,会对行加dml写锁,这个DML读锁是互斥的.其他dml写锁需要等待 ...

  2. 步步深入:MySQL架构总览->查询执行流程->SQL解析顺序

    前言: 一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎样的,然后查看总结各方资料,就有了下面这一篇博文了. 本文将从MySQL总体架构--->查询执行流程--->语句执行顺序来 ...

  3. MySQL架构总览->查询执行流程->SQL解析顺序

    Reference:  https://www.cnblogs.com/annsshadow/p/5037667.html 前言: 一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎样的,然后 ...

  4. 步步深入MySQL:架构->查询执行流程->SQL解析顺序!

    一.前言 一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎样的,然后查看总结各方资料,就有了下面这一篇博文了. 本文将从MySQL总体架构--->查询执行流程--->语句执行顺序 ...

  5. Mysql修改语句的运行流程

    执行修改语句前要先连接数据库,这是连接器的工作. 接下来,分析器会通过词法和语法解析知道这是一条更新语句.优化器决定要使用 ID 这个索引.然后,执行器负责具体执行,找到这一行,然后更新. Mysql ...

  6. 让MySQL为我们记录执行流程

    让MySQL为我们记录执行流程   我们可以开启profiling,让MySQL为我们记录SQL语句的执行流程   查看profiling参数 shell > select @@profilin ...

  7. 1020关于mysql一个简单语句的执行流程

    MySQL的语句执行顺序 转自http://www.cnblogs.com/rollenholt/p/3776923.html MySQL的语句一共分为11步,如下图所标注的那样,最先执行的总是FRO ...

  8. Mysql查询语句的运行流程

    我们先看一下MYsql的基本架构示意图: 大体来说,MySQL 可以分为 Server 层和存储引擎层两部分. Server 层包括连接器.查询缓存.分析器.优化器.执行器等,涵盖 MySQL 的大多 ...

  9. mysql查询语句的执行顺序(重点)

    一 SELECT语句关键字的定义顺序 SELECT DISTINCT <select_list> FROM <left_table> <join_type> JOI ...

随机推荐

  1. Heartbeat+HAProxy+MySQL半复制高可用架构

    目录 一 基础环境 二 架构设计 三 安装MySQL 3.1 安装MySQL 3.2 初始化MySQL 四 配置MySQL半同步 4.1 加载插件 4.2 配置半同步复制 4.3 master创建账号 ...

  2. Shell脚本基础及基本常用命令

    1.概述 脚本语言(shell.python):解释性语言,用解释器解释 运行效率低 | c.java:描述性语言,运行效率高 以.sh结尾会有高亮显示 执行: sh hello.sh 或者 chmo ...

  3. golang redis

    安装 下载第三方包: go get -u github.com/go-redis/redis/v9 连接 // 定义一个rdis客户端 var redisdb *redis.Client // 初始化 ...

  4. IPsec 9个包分析(主模式+快速模式)

    第一阶段:ISAKMP协商阶段 1.1 第一包 包1:发起端协商SA,使用的是UDP协议,端口号是500,上层协议是ISAKMP,该协议提供的是一个框架,里面的负载Next payload类似模块,可 ...

  5. Python - 虚拟环境 venv

    什么是虚拟环境 这是 Python 3.3 的新特性:https://www.python.org/dev/peps/pep-0405/ 假设自己电脑主机的 Python 环境称为系统环境,而默认情况 ...

  6. windows下配置VSCode免密SSH连接Linux机器

    先决条件 Windows下安装openssh软件(win10自带,可以不用搞) 从官网下载最新版本默认安装即可 VSCode安装插件 VSCode官方市场获取两个插件:"Remote - S ...

  7. Electron-vue项目使用 Inno Setup 创建安装包

    1.安装 Inno Setup 官网:https://jrsoftware.org/isinfo.php 2.打开 Inno Setup ,点击如下图Compli32.exe(首次安装默认打开) 3. ...

  8. JDK源码阅读:Object类阅读笔记

    Object 1. @HotSpotIntrinsicCandidate @HotSpotIntrinsicCandidate public final native Class<?> g ...

  9. 双非Java的学习之旅以及秋招路程

    个人信息: 趁着中秋写个帖子记录一下吧.渣渣本,无实习,无高质量证书,走了很多弯路,最后选择的Java后端.现在算是半躺平了,接了几个中小厂的offer保底,20w多的薪资,后面还有几家公司接着面.不 ...

  10. 288 day05_异常,线程

    day05 [异常.线程] 主要内容 异常.线程 教学目标 [ ] 能够辨别程序中异常和错误的区别 [ ] 说出异常的分类 [ ] 说出虚拟机处理异常的方式 [ ] 列举出常见的三个运行期异常 [ ] ...