MySQL之查询性能优化(六)

查询性能优化

查询优化器

  • 9.等值传播
    如果两个列的值通过等式关联,那么MySQL能够把其中一个列的WHERE条件传递到另一列上。例如,我们看下面的查询:
mysql> SELECT film.film_id FROM film-> INNER JOIN film_actor USING(film_id)-> WHERE film_id > 500;

因为这里使用了film_id字段进行等值关联,MySQL知道这里的WHERE子句不仅适用于film表,而且对于film_actor表同样适用。如果适用的是其他的数据库管理系统,可能还需要手动通过一些条件来告知优化器这个WHERE条件适用于两个表,那么写法就会如下:

... WHERE film.film_id > 500 AND film_actor.film_id > 500

在MySQL中这是不必要的,这样写反而会让查询更难维护。

  • 10.列表IN()的比较
    在很多数据库系统中,IN()完全等同于多个OR条件的子句,因为这两者是完全等价的。在MySQL中这点是不成立的,MySQL将IN()列表中的数据先进行排序,然后通过二分查找的方式来确定列表中的值是否满足条件,这是一个O(logn)复杂度的操作,等价地转换成OR查询的复杂度为O(n),对于IN()列表中有大量取值的时候,MySQL的处理速度将会更快。

上面列举的远不是MySQL优化器的全部,MySQL还会做大量其他的优化。上面的例子已经足以说明优化器的复杂性和智能性了。如果说从上面的讨论中应该学到什么,那就是"不要自以为比优化器更聪明"。最终你可能会占点便宜,但是更有可能会使查询变得更加复杂而难以维护,而最终的收益却未零。让优化器按照它的方式工作就可以了。当然,虽然优化器已经很智能了,但是有时候也无法给出最优的结果。有时候你可能比优化器更了解数据,例如,由于应用逻辑使得某些条件总是成立;还有时,优化器缺少某种功能特性,如哈希索引;再如前面提到的,从优化器的执行成本角度评估出来的最优执行计划,实际运行中可能比其他的执行计划更慢。如果能够确认优化器给出的不是最佳选择,并且清楚背后的原理,那么也可以帮助优化器做进一步的优化。例如,可以在查询中添加hint提示,也可以重写查询,或者重新设计更优的库表结构,或者添加更合适的索引

数据和索引的统计信息

在这里插入图片描述

重新回忆一下MySQL的架构,MySQL架构由多个层次组成。在服务器层有查询优化器,却没有保存数据和索引的统计信息。统计信息由存储引擎实现,不同的存储引擎可能会存储不同的统计信息(也可以按照不同的格式存储统计信息)。某些引擎,例如Archive引擎,则根本没有存储任何统计信息。因为服务器层没有任务统计信息,所以MySQL查询优化器在生成查询的执行计划时,需要向存储引擎获取相应的统计信息。存储引擎则提供给优化器对应的统计信息,包括:每个表或者索引有多少个页面、每个表的每个索引的基数是多少、数据行和索引长度、索引的分布信息等。优化器根据这些信息来选择一个最优的执行计划。

MySQL如何执行关联查询

MySQL中"关联"(join)一次所包含的意义比一般意义上理解的要更广泛。总的来说,MySQL认为任何一个查询都是一次"关联"——并不仅仅是一个查询需要到两个表匹配才叫关联,所以在MySQL中,每一个查询,每一个片段(包括子查询,甚至基于单表的SELECT)都可能是关联。所以,理解MySQL如何执行关联查询至关重要。我们先来看一个UNION查询的例子,对于UNION查询,MySQL先将一系列的单个查询结果放到一个临时表中,然后再重新读出临时表数据来完成UNION查询。在MySQL的概念中,每个查询都是一次关联,所以读取结果临时表也是一次关联。当前MySQL关联执行的策略很简单:MySQL对任何关联都执行嵌套循环关联操作,即MySQL先在一个表中循环取出单条数据,然后再嵌套循环到下一个表中寻找匹配的行,依次下去,知道找到所有表中匹配的行为止。然后根据各个表匹配的行,返回查询中需要的各个列。MySQL会尝试在最后一个关联表中找到所有匹配的行,如果最后一个关联表无法找到更多的行以后,MySQL返回上一层次关联表,看是否能够找到更多的匹配记录,依此类推迭代执行。按照这样的方式查找第一个表记录,再嵌套查询下一个关联表,然后回溯到上一个表,在MySQL中是通过嵌套循环的方式实现——正如其名"嵌套循环关联"。请看下面的例子中的简单查询:

SELECT
tbl1.col1,
tbl2.col2
FROM
tbl1
INNER JOIN tbl2 USING ( col3 )
WHERE
tbl1.col1 IN (5,6)

假设MySQL按照查询中的表顺序进行关联,我们则可以用下面的伪代码表示MySQL将如何完成这个查询:

outer_iter = iterator over tbl1 where col1 IN (5,6)
outer_row = outer_iter.next
while outer_row
inner_iter = iterator over tbl2 where col3 = outer_row.col3
inner_row = inner_iter.next
while inner_now
output [outer_row.col1, inner_row.col2]
inner_row = inner_iter.next
endouter_row = outer_iter.next
end

上面的执行计划对于单表查询和多表关联查询都适用,如果是一个单表查询,那么只需完成上面外层的基本操作。对于外连接上面的执行过程仍然适用。例如,我们将上面查询修改如下:

SELECT
tbl1.col1,
tbl2.col2
FROM
tbl1
LEFT OUTER JOIN tbl2 USING ( col3 )
WHERE
tbl1.col1 IN (5,6)

对应的伪代码如下,

outer_iter = iterator over tbl1 where col1 IN (5,6)
outer_row = outer_iter.next
while outer_row
inner_iter = iterator over tbl2 where col3 = outer_row.col3
inner_row = inner_iter.next
if inner_row
while inner_row
output [outer_row.col1, inner_row.col2]
inner_row = inner_iter.next
end
else
output [outer_row.col1, NULL]
end
outer_row = outer_iter.next
end

在这里插入图片描述

另一种可视化查询执行计划的方法是根据优化器执行的路径绘制出对应的"泳道图"。如图所示,绘制了前面示例中内连接的泳道图。如图所示,从本质上说,MySQL对所有的类型的查询都以同样的方式运行。例如,MySQL在FROM子句中遇到子查询时,先执行子查询并将其结果放到一个临时表中(MySQL的临时表是没有任何索引的,在编写复杂的子查询和关联查询的时候需要注意这一点。这一点对UNION查询也一样)。然后将这个临时表当作一个普通表对待(正如其名"派生表")。MySQL在执行UNION查询时也使用类似的临时表,在遇到右外连接的时候,MySQL将其改写成等价的左外连接。简而言之,当前版本的MySQL会将所有的查询类型都转换成类似的执行计划。不过,不是所有的查询都可以转换成上面的形式。例如,全外连接就无法通过嵌套玄幻和回溯的方式完成,这是当发现关联表中没有找到任何匹配行的时候,则可能是因为关联是恰好从一个没有任何匹配的表开始。这大概也是MySQL并不支持全外连接的原因,还有些场景,虽然可以转换成嵌套循环的方式,但是效率却非常差。

执行计划

在这里插入图片描述

在这里插入图片描述

和很多其他关系数据库不同,MySQL并不会生成查询字节码执行查询。MySQL生成查询的一棵指令书,然后通过存储引擎执行完成这棵指令树并返回结果。最终的执行计划包含了重构查询的全部信息。如果对某个查询执行EXPLAIN EXTENDED后,再执行SHOW WARNINGS,就可以看到重构出的查询。(MySQL根据执行计划生成输出。这和原查询有完全相同的语义,但是查询语句可能并不完全相同)。任何多表查询都可以使用一棵树表示,例如,可以按照如图所示执行一个四表的关联操作.在计算机科学中,这被成为一棵平衡树。但是,这并不是MySQL执行查询的方式。正如前面提到的,MySQL总是会从一个表开始一直嵌套循环、回溯完成所有表关联。所以,MySQL的执行计划总是如图所示,是一棵左侧深度优先的树

关联查询优化器

MySQL有优化器最重要的一部分就是关联查询优化,它决定了多个表关联时的顺序。通常多表关联的时候,可以有多种不同的关联顺序来获得相同的执行结果。关联查询优化器则通过评估不同顺序时的成本来选择一个代价最小的关联顺序。下面的查询可以通过不同顺序的关联最后都获得相同的结果:

SELECT
film.film_id,
film.title,
film.release_year,
actor.actor_id,
actor.first_name,
actor.last_name
FROM
sakila.film
INNER JOIN sakila.film_actor USING ( film_id )
INNER JOIN sakila.actor USING ( actor_id );

容易看出,可以通过一些不同的执行计划来完成上面的查询。例如,MySQL可以从film表开始,使用film_actor表的索引film_id来查找对应的actor_id值,然后再根据actor表的主键找到对应的记录。Oracle用户会用下面的术语描述:“film表作为驱动表先查找film_actor表,然后以此结果为驱动表再查找actor表”。这样做效率应该会不错,我们再使用EXPLAIN 看看MySQL将如何执行这个查询:

*************************** 1. row ***************************id: 1select_type: SIMPLEtable: actorpartitions: NULLtype: ALL
possible_keys: PRIMARYkey: NULLkey_len: NULLref: NULLrows: 200filtered: 100.00Extra: NULL
*************************** 2. row ***************************id: 1select_type: SIMPLEtable: film_actorpartitions: NULLtype: ref
possible_keys: PRIMARY,idx_fk_film_idkey: PRIMARYkey_len: 2ref: sakila.actor.actor_idrows: 27filtered: 100.00Extra: Using index
*************************** 3. row ***************************id: 1select_type: SIMPLEtable: filmpartitions: NULLtype: eq_ref
possible_keys: PRIMARYkey: PRIMARYkey_len: 2ref: sakila.film_actor.film_idrows: 1filtered: 100.00Extra: NULL
3 rows in set, 1 warning (0.00 sec)

这和我们前面给出的执行计划完全不同。MySQL从actor表开始(我们从上面的EXPLAIN 结果的第一行输出可以看出这点)。然后与我们前面的计划按照相反的顺序进行关联。这样是否效率更高呢?我们来看看,我们先使用STRIGHT_JOIN关键字,按照我们之前的顺序执行,这里是对应的EXPLAIN输出结果:

*************************** 1. row ***************************id: 1select_type: SIMPLEtable: filmpartitions: NULLtype: ALL
possible_keys: PRIMARYkey: NULLkey_len: NULLref: NULLrows: 1000filtered: 100.00Extra: NULL
*************************** 2. row ***************************id: 1select_type: SIMPLEtable: film_actorpartitions: NULLtype: ref
possible_keys: PRIMARY,idx_fk_film_idkey: idx_fk_film_idkey_len: 2ref: sakila.film.film_idrows: 5filtered: 100.00Extra: Using index
*************************** 3. row ***************************id: 1select_type: SIMPLEtable: actorpartitions: NULLtype: eq_ref
possible_keys: PRIMARYkey: PRIMARYkey_len: 2ref: sakila.film_actor.actor_idrows: 1filtered: 100.00Extra: NULL
3 rows in set, 1 warning (0.00 sec)

我们来分析一下为什么MySQL会将关联顺序倒转过来:可以看到,关联顺序倒转后的第一个关联表只需要扫描很少的行数(严格来说,MySQL并不根据读取的记录来选择最优的执行计划。实际上MySQL通过预估需要读取的数据页来选择,读取的数据页越少越好。不过读取的记录数通常能够很好地反应一个查询的成本)。在两种关联顺序下,第二个和第三个关联表都是根据索引查询,速度都很快,不同地是需要扫描的索引项的数量是不同的:

  • 1.将film表作为第一个关联表时,会找到1000条记录,然后对film_actor和actor表进行嵌套循环查询
  • 2.如果MySQL选择首先扫描actor表,只会返回200条记录进行后面的嵌套循环查询。

换句话说,倒转的关联顺序会让查询进行跟梢的嵌套循环和回溯操作。为了验证优化器的选择是否正确,我们单独执行两个查询,并且看看对应的Last_query_cost状态值。我们看到倒转的关联顺序的预估成本为241,而原来的查询的预估成本为1154.
这个简单的例子主要想说明MySQL是如何选择合适的关联顺序来让查询执行的成本尽可能的低。重新定义关联的顺序是优化器非常重要的一部分功能。不过有的时候,优化器给出的并不是最优的关联顺序。这时可以使用STRAIGHT_JOIN关键字重写查询,让优化器按照你认为的最优的关联顺序执行——不过老实说,人的判断很难那么精准。绝大多数时候,优化器做出的选择都比普通人的判断要更准确。关联优化器会尝试在所有的关联顺序中选择一个成本最小的来生成执行计划树。如果可能,优化器会遍历每一个表然后逐个做嵌套循环计算每一棵可能的执行计划树的成本,最后返回一个最优的执行计划。
不过,糟糕的是,如果有超过N个表的关联,那么需要检查N的阶乘种关联顺序。我们称之为所有可能的执行计划的"搜索空间",搜索空间的增长速度非常快——例如,若是10个表的关联,那么共有3 628 800种不同的关联顺序!当搜索空间非常大的时候,优化器不可能注意评估每一种关联顺序的成本。这时,优化器选择使用"贪婪"搜索的方式查找"最优"的关联顺序。实际上,当需要关联的表超过optmizer_search_depth的限制的时候,就会选择"贪婪"搜索模式了。在MySQL这些年的发展过程重,优化器积累了很多"启发式"的优化策略来加速执行计划的生成。绝大多数情况下,这都是有效地,但因为不会去计算每一种关联顺序的成本,所以偶尔也会选择一个不是最优的执行计划。有时,各个查询的顺序并不能随意安排,这时关联优化器可以根据这些规则大大减少搜索空间,例如,左连接、相关子查询(后面将讨论子查询)。这是因为后面的表的查询需要依赖于前面表的查询结果。这种依赖关系通常可以帮助优化器大大减少需要扫描的的执行计划数量

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

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

相关文章

使用Hadoop MapReduce分析邮件日志提取 id、状态 和 目标邮箱

使用Hadoop MapReduce分析邮件日志提取 id、状态 和 目标邮箱 在大数据处理和分析的场景中,Hadoop MapReduce是一种常见且高效的工具。本文将展示如何使用Hadoop MapReduce来分析邮件日志,提取邮件的发送状态(成功、失败或退回)和…

企业微信hook接口协议,ipad协议http,内部联系人备注修改

内部联系人备注修改 参数名必选类型说明uuid是String每个实例的唯一标识,根据uuid操作具体企业微信 请求示例 {"uuid":"1688855749266556","vid":1688856554448765,"remark":"备注啦啦啦22222","des&quo…

软件测试——蓝桥杯笔记(自用)

Before和BeforeClass,在测试前,初始化Driver,BeforeClass适用于静态方法 After和AfterClass,在测试后,关闭Driver,AfterClass适用于静态方法 自动化测试记得使用BeforeClass,AfterClass 单元…

2004NOIP普及组真题 2. 花生采摘

线上OJ: 【04NOIP普及组】花生采摘 核心思想: 1、本题为贪心即可。 2、因为本题严格限制了顺序,所以先把每个节点的花生数量按降序排序。然后逐一判断下一个花生是否需要去采摘即可 3、每一次采摘完,记录耗时 t 以及采集的花…

手机相册的排列方式探讨

不论你是不是程序员,你一定留意过一个问题:相册 App 基本都将图片裁剪成了居中的 1:1 正方形。那么手机相册 App,为什么要将图片切割成 1:1 正方形,然后以网格排列?是行业标准吗? 自适应图片宽度的图库&a…

Stable Diffusion——四种模型 LoRA(包括LyCORIS)、Embeddings、Dreambooth、Hypernetwork

目前 Stable diffusion 中用到主要有四种模型,分别是 Textual Inversion (TI)以 Embeddings 为训练结果的模型、Hypernetwork 超网络模型、LoRA(包括 LoRA 的变体 LyCORIS)模型、Dreambooth 模型。 视频博主 koiboi 用…

Linux中Apache网站基于Http服务的访问限制(基于地址/用户)

🏡作者主页:点击! 👨‍💻Linux高级管理专栏:点击! ⏰️创作时间:2024年6月3日11点44分 🀄️文章质量:95分 为了更好地控制对网站资源的访问,可…

用 Axios 封装一个双 token 无感刷新

为什么要用双Token无感刷新,它解决了什么问题? 为了保证安全性,后端设置的Token不可能长期有效,过了一段时间Token就会失效。而发送网络请求的过程又是需要携带Token的,一旦Token失效,用户就要重新登陆&…

python书上的动物是啥

Python的创始人为Guido van Rossum。1989年圣诞节期间,在阿姆斯特丹,Guido为了打发圣诞节的无趣,决心开发一个新的脚本解释程序,做为ABC语言的一种继承。之所以选中Python作为程序的名字,是因为他是一个叫Monty Python…

C# 判断字符串不等于空的示例

在C#中,要判断一个字符串是否不等于空(即它既不是null也不是空字符串""),方法有如下几种,如下。 方法1 使用逻辑运算符和string.IsNullOrEmpty方法 string myString "123"; // 假设要检查的字…

重邮803计网概述

目录 一.计算机网络向用户提供的最重要的功能 二.互联网概述 1.网络的网络 2.计算机网络的概念 3. 互联网发展的三个阶段 4.制订互联网的正式标准要经过以下的四个阶段 5.互联网的组成(功能) 6.互联网功能 7.互联网的组成(物理&#…

java自学阶段二:JavaWeb开发50(Spring和Springboot学习)

Spring、Springboot基础知识学习 目录 学习目标Spring基础概念IOC控制反转DI依赖注入事务管理AOP面向切面编程Spring案例说明(Postman使用、Restful开发规范、lombok、Restful、nginx了解) 一:学习目标: 1)了解Sprin…

将小爱音箱接入 ChatGPT 和豆包ai改造成专属语音助手

这个GitHub项目,mi-gpt,旨在将小爱音箱和米家设备与ChatGPT和豆包集成,有效地将这些设备转变为个性化语音助手。以下是对其功能和设置的详细分析: 主要特点 角色扮演:该项目允许小爱适应不同的角色,如伴侣…

HCIA-WLAN实验-二层旁挂组网

目录 前言:拓扑说明创建新拓扑配置网络互通SW1上配置VLAN10 20 30SW1上放行对应的VLANSW2上创建vlan 10 20并在对应接口放行VLAN在AC上创建vlan10并放行对应接口在SW1上创建vlanif20和vlanif30,并配置对应的IP在AC上创建vlanif10并配置IP在路由器AR上配置…

形如SyntaxError: EOL while scanning string literal,以红色波浪线形式在Pycharm下出现

背景: 新手在学习Python时可能会出现如下图所示的报错 下面分情况教大家如何解决 视频教程【推荐】: 形如SyntaxError: EOL while scanning string literal,以红色波浪线形式在Pycharm下出现 过程: 问题概述: 简单…

[数据集][目标检测]道路圆石墩检测数据集VOC+YOLO格式461张1类别

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):462 标注数量(xml文件个数):462 标注数量(txt文件个数):462 标注类别…

基于阿里云服务网格流量泳道的全链路流量管理(三):无侵入式的宽松模式泳道

作者:尹航 在前文《基于阿里云服务网格流量泳道的全链路流量管理(一):严格模式流量泳道》、《基于阿里云服务网格流量泳道的全链路流量管理(二):宽松模式流量泳道》中,我们介绍了流…

wpf工程中加入Hardcodet.NotifyIcon.Wpf生成托盘

1、在项目中用nuget引入Hardcodet.NotifyIcon.Wpf。如下图所示。 2、在App.xaml中创建托盘界面&#xff0c;代码是写在 App.xaml 里面 注意在application中一定要加入这一行代码&#xff1a; xmlns:tb"http://www.hardcodet.net/taskbar" 然后在<Application.R…

【免费Web系列】JavaWeb实战项目案例七(项目结束)

这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r 登录认证 在前面的课程中&#xff0c;我们已经实现了部门管理、员工管理的基本功能&#xff0c;但是大家会发现&#xff0c;我们并没有登录&#xff0c;就直接访问到了Tlias智能学习辅助系统的后台。 这…

韩国Neowine推出第三代强加密芯片ALPU-CV

推出第三代加密芯片&#xff1b;是ALPU系列中的高端IC&#xff1b;是一款高性能车规级加密芯片&#xff1b;其加密性更强、低耗电、体积小&#xff1b;使得防复制、防抄袭板子的加密性能大大提升&#xff0c;该芯片通过《AEC-Q100》认证&#xff0c;目前已经在国产前装车辆配件…