【6】mysql查询性能优化-关联子查询

【README】

0. 先说结论:一般用inner join来改写in和exist,用left join来改写not in,not exist;(本文会比较内连接,包含in子句的子查询,exist的性能 )

1. 本文总结自高性能mysql 6.5.1章节【关联子查询】

2. 数据库表及数据特征:

  • wn_film_tbl: 电影表, 50w数据量;
  • wn_actor_tbl: 演员表, 50w数据量;
  • wn_file_actor_rel_tbl: 电影演员关联表, 10w数据量;

3. ddl如下:

CREATE TABLE `wn_film_tbl` (`film_id` varchar(20) COLLATE utf8mb4_general_ci NOT NULL COMMENT '电影id',`film_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '电影名称',`release_year` int(11) NOT NULL COMMENT '上映年份',`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,`last_modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`film_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='电影表'CREATE TABLE `wn_actor_tbl` (`actor_id` varchar(20) COLLATE utf8mb4_general_ci NOT NULL COMMENT '演员id',`actor_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '演员名称',`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,`last_modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`actor_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='演员表'CREATE TABLE `wn_film_actor_rel_tbl` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',`film_id` varchar(20) COLLATE utf8mb4_general_ci NOT NULL COMMENT '电影id',`actor_id` varchar(20) COLLATE utf8mb4_general_ci NOT NULL COMMENT '演员id',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,`last_modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`),KEY `idx_film_id` (`film_id`) COMMENT '电影id索引',KEY `idx_actor_id` (`actor_id`) COMMENT '演员id索引'
) ENGINE=InnoDB AUTO_INCREMENT=100001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='电影与演员关联表'

【1】查询某演员参演的电影清单

1. 具体的,查询 演员0420AAAID000099参演的电影清单,通过造数,该演员参演的电影有 15000部;

2. 几种不同的实现方式:

  • 方案1: 使用 包含in()子句的子查询(性能非常低,作为反例,供各位看官参考)
  • 方案2:使用内连接;
  • 方案3:使用exists;

【1.1】使用包含in()子句的子查询

1. sql如下(为了方便打印执行计划,这里仅查询一条#limit 1):

select sql_no_cache * 
from wn_film_tbl film
where film_id in (select film_id from wn_film_actor_rel_tbl  where actor_id='0420AAAID000099')
limit 1
;

2. 查看其执行计划及查询成本:

  • explain 查看执行计划;
  • show status like 'last_query_cost' 查询上一个sql的执行成本;(成本的最小单位是随机读取1个大小为4K的数据页 )

3. 执行计划剖析(执行计划列的含义,参见
mysql_explain执行计划字段解析-CSDN博客):

4. 关联表执行步骤:

  • 第1步: 扫描film_actor_rel_tbl 表的普通索引 idx_actor_id ,查询 actor_id 等于0420AAAID000099 的 电影演员关联记录,得到列表 result_list
  • 第2步:全表扫描查询结果result_list(猜测是在内存中,因为extra没有 using temporary);
  • 第3步:扫描film表的主键索引树,查询film表中film_id(主键) 等于 result_list#film_id 字段值的记录并返回;

5. 对于上述步骤的总结:mysql对任何关联都执行嵌套循环关联操作(即先查询 rel表,然后全表扫描rel表,循环遍历rel表,再在单次遍历中循环遍历 film表 );

  • 有个问题: 为什么不反着来? 先查询 film表,再在循环遍历film过程中,遍历rel表(动脑筋的时候到了);
  • 小表驱动大表原则;  为什么小表驱动大表,大表驱动小表不行吗?(如果理解了上面的问题,就可以推导出小表驱动大表的依据)

6. 嵌套循环关联查询步骤如下:

  • 查询外层表的数据行, 并构建外层表的迭代器;
  • 遍历外层表迭代器,获取单个外层表数据行(第1层循环)
    • 根据单个外层表数据行,查询内层表的数据行,并构建内层表迭代器;
    • 遍历内层表迭代器,获取单个内层表数据行(第2层循环)
      • 合并外层与内层表数据行,构建输出结果;
      • 内层表迭代器滑动;
  • 外层表迭代器滑动;


 【1.2】使用内连接

sql如下:

select sql_no_cache film.* 
from wn_film_tbl film
inner join wn_film_actor_rel_tbl rel on film.film_id = rel.film_id 
where rel.actor_id='0420AAAID000099'
limit 1

【敲黑板】

  • 显然,根据执行计划,我们看到,mysql先查询了rel 表,然后再查询了 film表,为什么?(id相同,则按照从上到下的顺序执行)
  • 为什么不是先查询film表,再查询rel表;

【1.3】使用exists

sql如下:

select sql_no_cache film.* 
from wn_film_tbl film
where exists (select * from wn_film_actor_rel_tbl rel where rel.actor_id='0420AAAID000099'and rel.film_id = film.film_id 
) 
limit 1;


【2】总结:

1. 根据 last_query_cost 可以看到: 内连接需要扫描 36572个数据页;in子句的子查询需要扫描42467个数据页,所以内连接性能  【优于】 in子句的子查询

2. 从扫描行数来看: 内连接性能优于 in子句的子查询, in子句的子查询 优于  exists

问题:为什么 exits子查询的 last_query_cost结果显示为0呢?没懂

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

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

相关文章

【前端开发基础知识快速入门】

前端开发基础知识&快速入门 一、VSCode 使用1.1 安装常用插件1.2 创建项目1.3 创建网页1.4 运行效果二、ES62.1 简介2.2 什么是 ECMAScript2.3 ES6 新特性2.3.1 let 声明变量2.3.2 const 声明常量(只读变量)2.3.3 解构表达式2.3.4 字符串扩展2.3.5 函数优化2.3.6 对象优化…

使用Android studio,安卓手机编译安装yolov8部署ncnn,频繁出现编译错误

从编译开始就开始出现错误,解决步骤: 1.降低graddle版本,7.2-bin --->>> 降低为 6.1.1-all #distributionUrlhttps\://services.gradle.org/distributions/gradle-7.2-bin.zip distributionUrlhttps\://services.gradle.org/di…

【04】JAVASE-循环语句【从零开始学JAVA】

Java零基础系列课程-JavaSE基础篇 Lecture:波哥 Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机,Java 仍是企业和开发人员的首选开发平台。…

解决双击PDF文件出现打印的问题【Adobe DC】

问题描述 电脑安装Adobe Acrobat DC之后,双击PDF文件就会出现打印,而无法直接打开。 右键PDF文件就会发现,第一栏出现的不是用Adobe打开,而是打印。 重装软件多次仍然无法解决。 原因 右键菜单被改写了。双击其实是执行右键菜…

条件编译 #和##运算符

目录 1. #运算符2. ##运算符3. 条件编译4. 题目分享总结 正文开始 前言: 本章为C语言语法完结撒花, 下文将进行C语言中#和##操作符以及条件编译的讲解, 来进一步让我们了解C语言. 作者主页: 酷酷学!!! 1. #运算符 #运算符将宏的⼀个参数转换为字符串字⾯量。它仅允许出现在带…

解锁ApplicationContext vs BeanFactory: 谁更具选择性?

目录 一、聚焦源码回顾 (一)源码分析和理解 (二)简短的回顾对比建议 二、ApplicationContext vs BeanFactory特性对比 (一)主要特性总结 (二)直接建议 三、案例简单说明 &am…

[华为OD] 给航天器一侧加装长方形或正方形的太阳能板 100

给航天器一侧加装长方形或正方形的太阳能板(图中的红色斜线区域),需要先安装两个支 柱(图中的黑色竖条),再在支柱的中间部分固定太阳能板。但航天器不同位置的支柱长度 不同,太阳能板的安装面…

神经网络中多层卷积的作用

在神经网络中采用多层卷积的目的是为了逐步提取和组合图像的抽象特征,从而更有效地学习数据的表示并执行复杂的任务。不同层的卷积具有不同的作用,从较低层次的特征(例如边缘、纹理)到较高层次的抽象特征(例如物体部件…

如何在Windows 11中安装或删除可选功能?这里提供详细步骤

序言 Windows 11提供了各种各样的功能,其中许多功能,如Linux的Windows子系统(WSL)和语言包,它默认情况下不会安装。你也可以删除默认情况下安装的功能,以下是如何以图形方式或从命令行执行此操作。 关于Windows 11中的可选功能,你需要了解的内容 还有其他添加和删除功…

ubuntu20 中设置桌面背景任务

1. 下载conky 使用 Conky 在 Ubuntu 中显示信息,例如你的阅读计划,可以分几个步骤来完成。Conky 是一款灵活的轻量级系统监视器,能够在桌面上显示各种信息。以下是基本的设置步骤: 安装 Conky 首先,你需要在 Ubuntu…

TR6 - Transformer实战 单词预测

🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 目录 理论知识关于数据集 Wikitext-2 模型结构代码实现0. 环境1. 加载数据集2. 模型搭建3. 创建模型4. 单轮训练和评估的流程5. 训练 模型效果总结与心得体会 …

文本溢出体验进阶:CSS 技巧实现单行/多行隐藏展示以及实际场景应用,确保内容可读性和布局整洁性

CSS文本溢出隐藏是一种常见的场景,它广泛应用于各种网页设计中,旨在确保内容的可读性和布局的整洁性,特别是在空间有限或需要适应不同屏幕尺寸的情况下。 一、文本溢出隐藏并显示省略号 1、单行文本溢出隐藏并显示省略号 对于单行文本&…

数据可视化———Tableau

基本认识: 维度:定性—字符串文本,日期和日期时间等等 度量:定量—连续值,一般属于数值 数据类型: 数值 日期/日期时间 字符串 布尔值 地理值 运算符 算数运算符:加减乘除,%取余,…

抖音 小程序 获取手机号 报错 getPhoneNumber:fail auth deny

这是因为 当前小程序没有获取 手机号的 权限 此能力仅支持小程序通过试运营期后可用,默认获取权限,无需申请; https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/guide/open-capabilities/acquire-phone-number-acqu…

多商家AI智能名片商城系统(开源版)——构建高效数字化商业新生态

一、项目概述 1、项目背景 1)起源 随着数字化时代的快速发展,传统名片和商城系统已经难以满足企业日益增长的需求。商家需要更高效、更智能的方式来展示自己的产品和服务,与消费者进行互动和交易。同时,开源技术的普及也为开发…

代码随想录算法训练营第四十六天| LeetCode139.单词拆分

一、LeetCode139.单词拆分 题目链接/文章讲解/视频讲解:https://programmercarl.com/0139.%E5%8D%95%E8%AF%8D%E6%8B%86%E5%88%86.html 状态:已解决 1.思路 单词明显就是物品,字符串s明显就是背包,那么问题就变成了物品能不能把背…

Jmeter之Beanshell详解

一、 Beanshell概念 Beanshell: BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己的一些语法和方法;BeanShell是一种松散类型的脚本语言(这点和JS类似);BeanShell是用Java写成的,一个小型的、免费的、可以下载的、嵌入式的Java源代码解释器,具有对象脚本语言特性…

【项目实战】基于高并发服务器的搜索引擎

【项目实战】基于高并发服务器的搜索引擎 目录 【项目实战】基于高并发服务器的搜索引擎搜索引擎部分代码index.htmlindex.hpplog.hppparser.cc(用于对网页的html文件切分且存储索引关系)searcher.hpputil.hpphttp_server.cc(用于启动服务器和…

plsql 新建sql窗口 初始化慢的问题

问题描述: 新建sql窗口当sql语句多的情况下初始化很慢。 解决方法: 采用导入表的方式。 具体方式 工具->导入表->sql插入。 使用命令窗口 导入文件,然后点击导入按钮。

SpringBoot学习之SpringBoot3集成OpenApi(三十八)

Springboot升级到Springboot3以后,就彻底放弃了对之前swagger的支持,转而重新支持最新的OpenApi,今天我们通过一个实例初步看看OpenApi和Swagger之间的区别. 一、POM依赖 我的POM文件如下,仅作参考: <?xml version="1.0" encoding="UTF-8"?>…