【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.hqwc.cn/news/635882.html

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

相关文章

Spark-机器学习(3)回归学习之线性回归

在之前的文章中,我们了解我们的机器学习,了解我们spark机器学习中的特征提取和我们的tf-idf,word2vec算法。想了解的朋友可以查看这篇文章。同时,希望我的文章能帮助到你,如果觉得我的文章写的不错,请留下你…

异常检测 | SVDD支持向量数据描述异常数据检测(Matlab)

异常检测 | SVDD支持向量数据描述异常数据检测(Matlab) 目录 异常检测 | SVDD支持向量数据描述异常数据检测(Matlab)效果一览基本介绍程序设计参考资料 效果一览 基本介绍 用于一类或二元分类的 SVDD 模型 多种核函数(…

栈与堆的比较

栈与堆的比较 栈与堆的比较申请后系统的响应申请效率的比较申请大小的限制堆和栈中的存储内容总结参考 栈与堆的比较 内存布局: 申请后系统的响应 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异 常提示…

InnoDB架构:磁盘篇

InnoDB架构:磁盘篇 InnoDB是MySQL数据库中默认的存储引擎,它为数据库提供了事务安全型(ACID兼容)、行级锁定和外键支持等功能。InnoDB的架构设计优化了对于读取密集和写入密集型应用的性能表现,是一个高度优化的存储系…

【经典小游戏】猜数字

前言1. 游戏介绍2. 游戏实现3. 游戏优化结语 个人主页:C_GUIQU 前言 各位小伙伴大家好! 先问大家一个问题:我们为什么要学习? 简单来说,就是为了实践!只有不断学习才可以帮助我们更好地实践! 小…

驱动开发-windows驱动设计目标

驱动程序和应用程序不一样的,由于其直接运行于windows r0级,故对于开发有更多和更严格的标准,一般会有以下一些常见的设计目标: 安全性、可移植性、可配置性、 可被中断、多处理器安全、可重用 IRP、 支持异步 I/O这些是基本目标。 1. 安全…

稀碎从零算法笔记Day53-LeetCode:不同路径 II

稀碎系列有点更不动(更多是自己懈怠了) 题型:矩阵、模拟 链接:63. 不同路径 II - 力扣(LeetCode) 来源:LeetCode 题目描述 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” &…

C++ 深入理解 继承

本篇文章将谈谈一下几个问题: 1.基类和派生类对象赋值转换 2.继承中的作用域 3.派生类的默认成员函数 4.复杂的菱形继承及菱形虚拟继承 5.其他 1.基类和派生类对象赋值转换 1.派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切…

VSCode 目录折叠展开、缩进深度设置

1、VSCode 目录折叠展开设置 运行 Visual Studio Code ,按 Ctrl ,打开设置 输入Explorer:Compact Folders,取消勾选 或者在设置文件上添加 "explorer.compactFolders": false2、VSCode 目录缩进深度设置 输入Workbench Tree:…

STM32学习和实践笔记(18):STM32的外部中断的配置

如上图,EXTI的0-15是分配给GPIO的16个引脚。 但是GPIO差不多每个端口都有16个引脚,具体是怎样分配的呢? 很简单,就是通过AFIO寄存器来选择和分配。 比如,EXTI0,可以分配给GPIOA0,GPIOB0...GP…

Windows COM技术:COM介绍、代码演示。

目录 步骤一:理解COM技术 介绍COM的基础知识 1. COM的目的和特点 2. COM的关键概念 3. COM的实现 4. COM与DCOM、ActiveX 讨论COM的用途 1. 软件自动化 2. 插件和扩展 3. 跨语言开发 4. 分布式计算 5. 系统级组件 6. 网络浏览器插件 步骤二&#xff1a…

【嵌入式Linux】STM32P1开发环境搭建

要进行嵌入式Linux开发,需要在Windows、Linux和嵌入式Linux3个系统之间来回跑,需要使用多个软件工具。经过了4小时的安装(包括下载时间),我怕以后会忘记,本着互利互助的原则,我打算把这些步骤详…