架构师系列-MYSQL调优(五)- JOIN、in及exists优化

JOIN算法原理

JOIN 是 MySQL 用来进行联表操作的,用来匹配两个表的数据,筛选并合并出符合我们要求的结果集。JOIN 操作有多种方式,取决于最终数据的合并效果。常用连接方式的有以下几种:

 驱动表的定义

什么是驱动表 ?

  • 多表关联查询时,第一个被处理的表就是驱动表,使用驱动表去关联其他表.
  • 驱动表的确定非常的关键,会直接影响多表关联的顺序,也决定后续关联查询的性能

驱动表的选择要遵循一个规则:

  • 在对最终的结果集没有影响的前提下,优先选择结果集最小的那张表作为驱动表

 三种JOIN算法

1.Simple Nested-Loop Join( 简单的嵌套循环连接 )

  • 简单来说嵌套循环连接算法就是一个双层for 循环 ,通过循环外层表的行数据,逐个与内层表的所有行数据进行比较来获取结果.

  • 这种算法是最简单的方案,性能也一般。对内循环没优化。

  • 例如有这样一条SQL:

-- 连接用户表与订单表 连接条件是 u.id = o.user_id
select * from user t1 left join order t2 on t1.id = t2.user_id;
-- user表为驱动表,order表为被驱动表转换成代码执行时的思路是这样的: 
for(user表行 uRow : user表){for(Order表的行 oRow : order表){if(uRow.id = oRow.user_id){return uRow;}}
}

匹配过程如下图

  • SNL 的特点

    • 简单粗暴容易理解,就是通过双层循环比较数据来获得结果

    • 查询效率会非常慢,假设 A 表有 N 行,B 表有 M 行。SNL 的开销如下:

      • A 表扫描 1 次。
      • B 表扫描 M 次。
      • 一共有 N 个内循环,每个内循环要 M 次,一共有内循环 N * M 次

2) Index Nested-Loop Join( 索引嵌套循环连接 ) 

  • Index Nested-Loop Join 其优化的思路: 主要是为了减少内层表数据的匹配次数 , 最大的区别在于,用来进行 join 的字段已经在被驱动表中建立了索引。

  • 从原来的 匹配次数 = 外层表行数 * 内层表行数 , 变成了 匹配次数 = 外层表的行数 * 内层表索引的高度 ,极大的提升了 join的性能。

  • 当 order 表的 user_id 为索引的时候执行过程会如下图:

注意:使用Index Nested-Loop Join 算法的前提是匹配的字段必须建立了索引。

 3) Block Nested-Loop Join( 块嵌套循环连接 )

  • 如果 join 的字段有索引,MySQL 会使用 INL 算法。如果没有的话,MySQL 会如何处理?

  • 因为不存在索引了,所以被驱动表需要进行扫描。这里 MySQL 并不会简单粗暴的应用 SNL 算法,而是加入了 buffer 缓冲区,降低了内循环的个数,也就是被驱动表的扫描次数。

  • 在外层循环扫描 user表中的所有记录。扫描的时候,会把需要进行 join 用到的列都缓存到 buffer 中。buffer 中的数据有一个特点,里面的记录不需要一条一条地取出来和 order 表进行比较,而是整个 buffer 和 order表进行批量比较。

  • 如果我们把 buffer 的空间开得很大,可以容纳下 user 表的所有记录,那么 order 表也只需要访问一次。

  • MySQL 默认 buffer 大小 256K,如果有 n 个 join 操作,会生成 n-1 个 join buffer。

mysql> show variables like '%join_buffer%';
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| join_buffer_size | 262144 |
+------------------+--------+mysql> set session join_buffer_size=262144;
Query OK, 0 rows affected (0.00 sec)

总结

  1. 永远用小结果集驱动大结果集(其本质就是减少外层循环的数据数量)

  2. 为匹配的条件增加索引(减少内层表的循环匹配次数)

  3. 增大join buffer size的大小(一次缓存的数据越多,那么内层包的扫表次数就越少)

  4. 减少不必要的字段查询(字段越少,join buffer 所缓存的数据就越多

in和exists函数 

上面我们说了 小表驱动大表,就是小的数据集驱动大的数据集, 主要是为了减少数据库的连接次数,根据具体情况的不同,又出现了两个函数 exists 和 in 函数

创建部门表与员工表,并插入数据

CREATE TABLE department (id INT(11) PRIMARY KEY,deptName VARCHAR(30) ,address VARCHAR(40) 
) ;-- 部门表测试数据
INSERT INTO `department` VALUES (1, '研发部', '1层');
INSERT INTO `department` VALUES (2, '人事部', '3层');
INSERT INTO `department` VALUES (3, '市场部', '4层');
INSERT INTO `department` VALUES (5, '财务部', '2层');-- 员工表
CREATE TABLE employee (id INT(11) PRIMARY KEY,NAME VARCHAR(20) ,dep_id INT(11) ,age INT(11) ,salary DECIMAL(10, 2)
);-- 员工表测试数据
INSERT INTO `employee` VALUES (1, '鲁班', 1, 15, 1000.00);
INSERT INTO `employee` VALUES (2, '后裔', 1, 22, 2000.00)
INSERT INTO `employee` VALUES (4, '阿凯', 2, 20, 3000.00);
INSERT INTO `employee` VALUES (5, '露娜', 2, 30, 3500.00);
INSERT INTO `employee` VALUES (6, '李白', 3, 25, 5000.00);
INSERT INTO `employee` VALUES (7, '韩信', 3, 50, 5000.00);
INSERT INTO `employee` VALUES (8, '蔡文姬', 3, 35, 4000.00);
INSERT INTO `employee` VALUES (3, '孙尚香', 4, 20, 2500.00);

1) in 函数

  • 假设: department表的数据小于 employee表数据, 将所有部门下的员工都查出来,应该使用 in 函数
SELECT * FROM employee e WHERE e.dep_id IN (SELECT id FROM department);

in函数的执行原理

  1. in 语句, 只执行一次, 将 department 表中的所有id字段查询出来并且缓存.
  2. 检查 department 表中的id与 employee 表中的 dep_id 是否相等, 如果相等 添加到结果集, 直到遍历完department 所有的记录.

-- 先循环: select id from department; 相当于得到了小表的数据
for(i = 0; i < $dept.length; i++){  -- 小表-- 后循环: select * from employee where e.dep_id  = d.id;for(j = 0 ; j < $emp.legth; j++){  -- 大表if($dept[i].id == $emp[j].dep_id){$result[i] = $emp[j]break;}}
}
  • 结论: 如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用 in

2) exists 函数 

假设: department表的数据大于 employee表数据, 将所有部门下的的员工都查出来,应该使用 exists 函数. 

explain SELECT * FROM employee e WHERE EXISTS (SELECT id FROM department d WHERE d.id = e.dep_id);

exists 特点

exists 子句返回的是一个 布尔值,如果有返回数据,则返回值是true,反之是false。

如果结果为 true , 外层的查询语句会进行匹配,否则 外层查询语句将不进行查询或者查不出任何记录。

 exists 函数的执行原理

-- 先循环: SELECT * FROM employee e;
-- 再判断: SELECT id FROM department d WHERE d.id = e.dep_idfor(j = 0; j < $emp.length; j++){  -- 小表-- 遍历循环外表,检查外表中的记录有没有和内表的的数据一致的, 匹配得上就放入结果集。if(exists(emp[i].dep_id)){   -- 大表$result[i] = $emp[i];}
}

3) in 和 exists 的区别

  • 如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用 in

  • 如果主查询得出的结果集记录较少,子查询中的表较大且又有索引时应该用 exists

  • 一句话: in后面跟的是小表,exists后面跟的是大表。

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

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

相关文章

Bert语言大模型基础

一、Bert整体模型架构 基础架构是transformer的encoder部分&#xff0c;bert使用多个encoder堆叠在一起。 主要分为三个部分&#xff1a;1、输入部分 2、注意力机制 3、前馈神经网络 bertbase使用12层encoder堆叠在一起&#xff0c;6个encoder堆叠在一起组成编码端&#xf…

将一个整数输出为质因子相乘的形式

【题目描述】 将一个整数输出为质因子相乘的形式。 例如&#xff1a;输入12&#xff0c;输出 2*2*3。【算法分析】 ○ 若 n 是合数&#xff0c;则在 1~sqrt(n) 范围内进行因子判别。简证如下&#xff1a; 给定一个数字 n&#xff0c;朴素的求其因子的方法为枚举 [1,n] 的所有数…

Podman容器的原理及应用详解(二)

本系列文章简介&#xff1a; Podman是一个用于管理容器的工具&#xff0c;它提供了一种在Linux系统中运行和管理容器的替代方案。与传统的容器管理工具Docker不同&#xff0c;Podman使用了一种不需要守护进程的架构&#xff0c;这使得它更加轻量化、安全和易于使用。 Podman的核…

Spring Boot中判断轨迹数据是否经过设置的打卡点,且在PGSQL中把点拼接成线,判断某个点是否在线上或在线的50米范围内

问题描述 轨迹数据判断是否经过打卡点&#xff0c;轨迹数据太多&#xff0c;循环判断的话非常消耗内存。解决办法只需要把所有轨迹数据点拼成了一条线&#xff0c;然后只需要循环打卡点即可&#xff0c;打卡点不多&#xff0c;一般不会超过100个&#xff0c;如果多的话&#x…

asp.net get请求base64解密报错问题

刚开始没编码&#xff0c;使用encodeURIComponent进行了编码&#xff0c;但是后台解码会被解析为空格&#xff0c;最后使用hex解决 public class HexConverter {/// <summary>/// 转换十六进制字符串到字节数组/// </summary>/// <param name"msg"&g…

R可视化:桑基图展示数据层流动

介绍 以桑基图形式展示数据分布情况 加载R包 knitr::opts_chunk$set(message = FALSE, warning = FALSE) library(tidyverse) library(ggalluvial)# rm(list = ls()) options(stringsAsFactors = F) options(future.globals.maxSize = 10000 * 1024^2) 导入数据 metadata…

从零开始精通RTSP之深入理解RTCP协议

概述 RTCP&#xff0c;即实时控制协议&#xff0c;英文全称为RTP Control Protocol&#xff0c;是RTP的配套协议。与RTP不同&#xff0c;RTCP本身不传输实时数据&#xff0c;而是用于提供有关RTP会话的统计信息和控制功能。RTCP的主要目标是提供数据传输质量的反馈&#xff0c;…

【计算机毕业设计】大学校园图书角管理系统——后附源码

&#x1f389;**欢迎来到我的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 一名来自世界500强的资深程序媛&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 在深度学习任务中展现出卓越的能力&#xff0c;包括但不限于…

【Flutter】One or more plugins require a higher Android SDK version.

问题描述 项目里多个组件需要更高版本的Android SDK One or more plugins require a higher Android SDK version.解决方案&#xff1a; 报错提示requires Android SDK version 34 按提示修改android项目app里build.gradle的compileSdkVersion 为34 android {compileSdkVe…

Spring 数据脱敏实现方式

1、前言 当前互联网中&#xff0c;越来越重视数据安全&#xff0c;数据脱敏在实际应用中越来越多。 2 、脱敏方式 2.1 数据库sql 语句脱敏 sql 语句脱敏是比较传统通用的&#xff0c;例子如下所示&#xff1a; select CONCAT(LETF(mobile,3),"*****",RIGHT(mobile,…

node.js-包

包的概念 包&#xff1a;将模块&#xff0c;代码&#xff0c;其他资料聚合成的一个文件夹 包分类&#xff1a; 1.项目包&#xff1a;主要用于编写项目和业务逻辑的文件夹 2.软件包&#xff1a;封装工具和方法供开发者使用&#xff08;一般使用npm管理&#xff09; 1&#…

证明:每次循环执行i = (i-1)s可以枚举s表示集合的所有子集i

状态压缩&#xff1a;使用二进制数表示一个集合的情况&#xff0c;第i位为1表示第i元素在集合中&#xff0c;为0表示不在集合中。 已知i表示的集合是s表示的集合的子集&#xff0c;枚举s的所有子集i可以写为 for(int i s; i ! 0; i (i-1)&s){}证明&#xff1a;每次循环执…

SpringBoot之JdbcTemplate输出完整SQL日志

applicatio.yml开启日志功能 jdbc-log:# 开启完整SQL日志输出功能enabled: truelogging:level:# 切面类路径&#xff0c;日志级别为DEBUG&#xff0c;因为SpringBoot默认日志级别为INFOcom.xxx.xxx.JdbcTemplateAspect: DEBUG日志切面 import lombok.extern.slf4j.Slf4j; imp…

mysql的DDL语言和DML语言

DDL语言&#xff1a; 操作数据库&#xff0c;表等&#xff08;创建&#xff0c;删除&#xff0c;修改&#xff09;&#xff1b; 操作数据库 1&#xff1a;查询 show databases 2:创建 创建数据库 create database 数据库名称 创建数据库&#xff0c;如果不存在就创建 crea…

造成并发安全的三大源头:可见性、原子性、有序性

缓存导致的可见性问题 一个线程对共享变量的修改&#xff0c;另外一个线程能够立刻看到&#xff0c;我们称为 可见性 如果是单核cpu&#xff0c;cpu之间的线程共享一个缓存&#xff0c;这个时候不会出现缓存与内存数据一致性的问题&#xff0c;同样的线程之间具备可见性 如果…

MySQL—一条查询SQL语句的完整执行流程

MySQL—一条查询SQL语句的完整执行流程 表结构和数据如下&#xff1a; 我们分析的sql语句如下&#xff1a; select tb_id,tb_name,tb_address from tb_user where tb_id 66;大体来说&#xff0c;MySQL可以分为Server层和存储引擎层两部分: Server层 包括:连接器、查询缓存、…

使用Java实现动态心形图案

一、引言 在计算机图形学中&#xff0c;动态图案的生成和显示一直是一个令人兴奋的话题。心形图案作为情感表达的一种常见方式&#xff0c;在编程领域也颇受欢迎。本文将介绍如何使用Java编程语言实现动态心形图案&#xff0c;并附上相应的代码片段。 二、心形曲线的数学表达…

如何使用 ArcGIS Pro 快速为黑白地图配色

对于某些拍摄时间比较久远的地图&#xff0c;限于当时的技术水平只有黑白的地图&#xff0c;针对这种情况&#xff0c;我们可以通过现在的地图为该地图进行配色&#xff0c;这里为大家讲解一下操作方法&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微…

dubbo3-01.helloworld

项目结构 - dubbo-examples- consumer-service- provider-interface- provider-servicepom 文件 dubbo-examples 的 pom.xml <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <arti…

使用大卫的k8s监控面板(k8s+prometheus+grafana)

问题 书接上回&#xff0c;对EKS&#xff08;AWS云k8s&#xff09;启用AMP&#xff08;AWS云Prometheus&#xff09;监控AMG(AWS云 grafana)&#xff0c;上次我们只是配通了EKSAMPAMG的监控路径。这次使用一位大卫老师的grafana的面板&#xff0c;具体地址如下&#xff1a; ht…