目录

  • Mysql优化(出自官方文档) - 第六篇

    • Optimizing Subqueries, Derived Tables, View References, and Common Table Expressions

Mysql优化(出自官方文档) - 第六篇

Optimizing Subqueries, Derived Tables, View References, and Common Table Expressions

对于子查询,Mysql通常使用如下的优化方式:

  • 对于IN(or =ANY)式的子查询,优化器使用如下方式:

    • semijoin
    • 物化
    • EXISTS策略
  • 对于NOT IN(OR <>ALL)式的子查询,优化器使用如下方式:
    • 物化
    • EXISTS策略

对于derived tables,优化器使用如下方式(对于view referencescommon table expressions也同样适用):

  • derived table合并到外部查询里
  • derived table物化为临时表。

特别的,对于带有子查询的UPDATEDELETE语句,优化方式为:

优化器不会使用semijoin和物化来优化这种情况,而是将其重写为多张表的UPDATEDELETE操作,同时使用join来代替子查询操作。

1 Optimizing IN and EXISTS Subquery predicates with Semijoin Transformations

假设有两张表,classroster,现在要查询有学生出勤课程的课程编号和课程名称,我们可以很简单的写出下面的语句:

SELECT class.class_num, class.class_name
FROM class INNER JOIN roster
WHERE class.class_num = roster.class_num;

假设class_numclass表的primary key,可以看出来,上面的查询结果中必然有重复列,因为多个学生可以出勤同一个课程,所以,为了去重,我们可以加上SELECT DISTINCT这样的限定。

除此之外,还可以将上面的join语句改为子查询的方式,如下:

SELECT class_num, class_name
FROM class
WHERE class_num IN (SELECT class_num FROM roster);

该语句有如下特点:

  • SELECT的目标只有一张表的列
  • IN表示在第二张表中只要有第一张表相同的值就立即返回

此时,Mysql会将上面的子查询优化为semijoinsemijoin的特点就是在join中一旦查询到匹配行,就立即只返回一张表的数据,后续重复的值将没有必要继续扫描。

同时,在Mysql8.0.17之后,下面的语句也会被转换为antijoin(和semijoin相反,当第一张表在第二张表中没有匹配行时,立即返回第一张表的列。)

  • NOT IN (SELECT ... FROM ...)
  • NOT EXISTS (SELECT ... FROM ...).
  • IN (SELECT ... FROM ...) IS NOT TRUE
  • EXISTS (SELECT ... FROM ...) IS NOT TRUE.
  • IN (SELECT ... FROM ...) IS FALSE
  • EXISTS (SELECT ... FROM ...) IS FALSE.

对于semijoin,Mysql主要的处理方式如下:

  • Duplicate Weedout
  • FirstMatch
  • LooseScan
  • Materialize

这四种的实现方式网上均有介绍,这里就不赘述了。

2 Optimizing Subqueries with Materialization

Mysql经常使用物化的方式来优化subquery,通常的方式是创建一个临时表(一般来讲是全内存临时表,只有当临时表变得比较大的时候,才会进行下盘处理),并且优化器会使用hash index的方式对临时表创建索引来加快查询,index的值是唯一的,所以能够避免重复的值。

对于下面的语句,如果不适用物化的话:

SELECT * FROM t1
WHERE t1.a IN (SELECT t2.b FROM t2 WHERE where_condition);

优化器会将该语句重写为:

SELECT * FROM t1
WHERE EXISTS (SELECT t2.b FROM t2 WHERE where_condition AND t1.a=t2.b);

这种有所关联的子查询语句(关联指子查询语句中不仅查询t2内表的数据,还会和t1外表有关), 这种类型的子查询的执行过程为:外表每执行一次,子查询就要执行一次,所以效率很低。

为了让Mysql使用物化来运行子查询,查询语句必须符合如下形式:

  • 外查询中oe_i和内查询ie_i不能为nullN1或者更大的值(??为什么?不是很理解?)

    (oe_1, oe_2, ..., oe_N) [NOT] IN (SELECT ie_1, i_2, ..., ie_N ...)
  • 外查询和内查询均只有一个表达式,表达式的值可以为null

    oe [NOT] IN (SELECT ie ...)
  • 谓词必须为in或者not in,或者和FALSE具有相同语义的表达式。

举例如下,下面的语句将会使用物化技术:

SELECT * FROM t1
WHERE t1.a IN (SELECT t2.b FROM t2 WHERE where_condition);

下面的语句将无法使用物化技术,因为t2.b可能为null

SELECT * FROM t1
WHERE (t1.a,t1.b) NOT IN (SELECT t2.a,t2.b FROM t2WHERE where_condition);

需要注意的是,对于列的类型信息,必须满足如下条件,才能使用物化技术:

  • 内查询和外查询的列必须匹配,比如如果一个integer另外一个是decimal,优化器将无法使用物化技术。
  • 内查询的表达式类型不能为BLOB,根据第一条的限制,外查询也同样不能为BLOB

在EXPLAIN的输出里面,如果使用了物化技术,那么输出如下:

  • select_type将由DEPENDENT SUBQUERY 变为SUBQUERY, DEPENDENT的意思是外查询每执行一次,内查询就要执行一次,使用物化技术的话,内查询只需要执行一次。
  • EXPLAIN的输出里面,SHOW WARNINGS会包含materializematerialized-subquery

3 Optimizing Subqueries with the EXISTS Strategy

对于如下的语句,如果不采取章节2中的优化方式,那么通常的执行方式是:外查询执行一次,内查询在执行一次。

outer_expr IN (SELECT inner_expr FROM ... WHERE subquery_where)

对于这种情况,由于只需要特定列,所以Mysql通常会使用条件下推的方式进行优化,优化后的结果为:

EXISTS (SELECT 1 FROM ... WHERE subquery_where AND outer_expr=inner_expr)

这样子,子查询的条件将更加严格,可以大大降低子查询需要行数。

同理,如果选取的是多列,那么也可以采用同样的优化方式:

(oe_1, ..., oe_N) IN(SELECT ie_1, ..., ie_N FROM ... WHERE subquery_where)

上面这条语句将会优化为:

EXISTS (SELECT 1 FROM ... WHERE subquery_whereAND oe_1 = ie_1AND ...AND oe_N = ie_N)

这种条件下推的方式有其局限性,如下:

  • outer_exprinner_expr均不能为NULL

  • 如果OR或者AND语句在WHERE语句中,Mysql假设用户并不关心返回的值是NULL还是FALSE,因此,对于下面的语句:

    ... WHERE outer_expr IN (subquery)

    无论子查询返回NULL还是返回FALSEWHERE都不会接受。

如果说,上面两条限制都不符合,那么此时的优化方式将会变的很复杂,主要分为以下两种情况:

  • 如果outer_expr不会有NULL产生,此时,outer_expr IN (SELECT ...)的结果分为以下两种情况:

    • 如果SELECTinner_expr is NULL的条件下返回了任意行,那么结果为NULL
    • 如果SELECT只返回了非NULL行或者说没有返回任何一行,那么结果为FALSE

    对于这种情况,当查找outer_expr = inner_expr时,如果没有找到,还需要查找inner_expr is NULL这样的列,因此,这种语句会被转换为下面的形式:

    EXISTS (SELECT 1 FROM ... WHERE subquery_where AND(outer_expr=inner_expr OR inner_expr IS NULL))

    EXPLAIN里面,这样的语句type列为: ref_or_null

  • 如果outer_expr有可能产生NULL列,那么情况将会变得比较复杂,对于NULL IN (SELECT inner_expr ...)这样的语句,结果分为两种情况:

    • 如果SELECT返回了任意行,那么结果为NULL
    • 如果SELECT没有返回行,那么结果为FALSE

    所以,优化器为了加快速度,需要分两种情况来处理:

    • 如果outer_expr的结果为NULL,就需要判断子查询是否返回任意行,此时无法使用条件下推的优化,这个时候的性能是最差的,外查询没查询一次,就需要执行一次子查询。

    • 如果outer_expr的结果不为NULL,那么就可以使用上面提到的条件下推这种优化方式,语句将会被重写为:

      EXISTS (SELECT 1 FROM ... WHERE subquery_where AND outer_expr=inner_expr)

      综上,为了包含上面两种情况,Mysql使用一种叫做"trigger"的函数(不同于数据库里面创建的trigger),在Mysql里面体现为Item_func_trig_cond类,因此,上面的两种情况都会统一被转换为:

      EXISTS (SELECT 1 FROM ... WHERE subquery_whereAND trigcond(outer_expr=inner_expr))

      同理,如果有多列,如下面的语句:

      (oe_1, ..., oe_N) IN (SELECT ie_1, ..., ie_N FROM ... WHERE subquery_where)

      将会被转换为:

      EXISTS (SELECT 1 FROM ... WHERE subquery_whereAND trigcond(oe_1=ie_1)AND ...AND trigcond(oe_N=ie_N))

      对于trigcond(x),Mysql的处理方式如下:

      • 如果外查询的结果不是NULL,那么trigcond的结果即为x的结果
      • 如果外查询的结果为NULL,那么trigcond返回TRUE(这里不是很理解,需要详细理解下)

      Note

      这里的trigger不同于平时使用sql创建的trigger``,CREATE TRIGGER

帮助优化器进行优化的一些技巧:

  • 如果某一列永远不会产生NULL列,那么将其声明为NOT NULL,这样子可以帮助优化器进行更进一步的优化。

  • 如果不需要区分NULLFALSE,对于下面的语句:

    outer_expr IN (SELECT inner_expr FROM ...)

    为了避免Mysql采用最糟糕的方式进行执行,可以将该语句修改为:

    (outer_expr IS NOT NULL) AND (outer_expr IN (SELECT inner_expr FROM ...))

    这样子Mysql可以使用短路判断尽快返回结果,可以大量的减少AND后面语句的执行次数。

    另外,也可以写成下面这样子:

    EXISTS (SELECT inner_expr FROM ...WHERE inner_expr=outer_expr)

4 Optimizing Derived Tables, View References, and Common Table Expressions with Merging or Materialization

在优化derived table的时候,通常采用两种策略(同样适用于view referencescommon table expressions):

  • derived table合并到外层的查询里面
  • 物化derived table到一个临时表里面

举例如下:

SELECT * FROM (SELECT * FROM t1) AS derived_t1;

会被优化为:

SELECT * FROM t1;

下面的语句:

SELECT *FROM t1 JOIN (SELECT t2.f1 FROM t2) AS derived_t2 ON t1.f2=derived_t2.f1WHERE t1.f1 > 0;

会被优化为:

SELECT t1.*, t2.f1FROM t1 JOIN t2 ON t1.f2=t2.f1WHERE t1.f1 > 0;

可以明显的看到,如果采取非物化的方式,执行效率将会大大提升。

优化器会将derived table里面的ORDER BY优化到外层查询语句中,但是,必须满足如下条件:

  • 外层查询没有使用group by或者聚合函数
  • 外层查询没有使用DISTINCT, HAVING, or ORDER BY
  • 外层查询只有在FROM中才使用到了derived table

如果优化器没办法使用MERGE,意味着只能使用物化为临时表的方式来执行,此时,为了加快效率,将采用如下的优化方式:

  • 优化器只有在需要derived table的时候才会进行物化操作,这样子有时候就可以避免进行物化操作。比如说:一个查询中子查询需要进行物化操作,条件里面有外表和dervied table的对比,此时先执行外查询,如果外查询返回了空行,这个时候,dervied table就没必要再继续执行了,可以减少没有必要的物化操作。
  • 在执行期间,优化器会根据需要给dervied table添加对应的索引,这样子可以提高dervied table的访问效率。

转载于:https://www.cnblogs.com/seancheer/p/11288623.html

Mysql优化(出自官方文档) - 第六篇相关推荐

  1. OpenGL ES着色器语言之语句和结构体(官方文档第六章)内建变量(官方文档第七、八章)...

    OpenGL ES着色器语言之语句和结构体(官方文档第六章) OpenGL ES着色器语言的程序块基本构成如下: 语句和声明 函数定义 选择(if-else) 迭代(for, while, do-wh ...

  2. Unity 优化翻译官方文档(二) ------ 平台特定覆盖的纹理压缩格式

    官方文档 : https://docs.unity3d.com/Manual/class-TextureImporterOverride.html 虽然Unity支持许多常见的图像格式作为导入纹理的源 ...

  3. Unity优化翻译官方文档(六) ------ CPU Usage Profiler

    官网地址 : https://docs.unity3d.com/Manual/ProfilerCPU.html CPU使用分析器显示在您的游戏中花费的时间.当它被选中时,下窗格将显示所选帧的分层时间数 ...

  4. 看懂mysql执行计划--官方文档

    原文地址:https://dev.mysql.com/doc/refman/5.7/en/explain-output.html 9.8.2 EXPLAIN Output Format The EXP ...

  5. storm mysql trident_Apache Storm 官方文档 —— Trident 教程

    Trident 是 Storm 的一种高度抽象的实时计算模型,它可以将高吞吐量(每秒百万级)数据输入.有状态的流式处理与低延时的分布式查询无缝结合起来.如果你了解 Pig 或者 Cascading 这 ...

  6. Unity 优化翻译官方文档(三) ------ Animation Clips

    动画剪辑是Unity动画中最小的组成部分.它们代表了一个孤立的运动,如RunLeft.跳转或爬行,并且可以以各种方式进行操作和组合,以产生生动的最终结果(参见动画状态机.动画控制器控制器或混合树).您 ...

  7. ABP官方文档(十六)【值对象】

    3.2 ABP领域层 - 值对象 3.2.1 简介 用来描述领域的特殊方面.且没有标识符的一个对象,叫做值对象. 实体有自己的唯一标识,而值对象是没有标识的.如果两个实体的标识是不同的,那么它们是两个 ...

  8. [WebApp开发]基础教程-Google官方文档-第四篇

    文档内容 在Android Browser中使用控制台API 在WebView中使用控制台API 参考 调试 如果你是在为Android开发web应用,那么,你可以使用控制台(console)的Jav ...

  9. 【六袆 - MySQL】MySQL5.7官方文档;MySQL5.7英文版;MySQL5.7英文版翻译成中文;

    MySQL5.7英文版翻译成中文 第7章目录页 第8章目录页 第9章目录页 第11章目录页 第12章目录页 第13章目录页 第14章目录页 第14-2章目录页 第15章目录页 第17章目录页 第24章 ...

  10. mysql中括号_手把手教你看MySQL官方文档

    前言: 在学习和使用MySQL的过程中,难免会遇到各种问题.不知道当你遇到相关问题时会怎么做,我在工作或写文章的过程中,遇到不懂或需要求证的问题时通常会去查阅官方文档.慢慢的,阅读文档也有了一些经验, ...

最新文章

  1. toeplitz--生成托普利兹矩阵
  2. tomcat 终止端口号时 taskkill /pid 17292 /f 中/f是什么意思
  3. 希尔排序基础java代码_java 算法之希尔排序详解及实现代码
  4. C#10 和 .NET6 代码跨平台开发
  5. matlab simplify,[求助]Matlab2016b里没有simple函数
  6. vue 方法获取返回值_vue中子组件怎么获取父组件中的方法的返回值
  7. 【Kafka】kafka Current offset xxx for partition xxx out range
  8. 用户密码重设对EFS的影响
  9. 【吾日三省吾身】2015.6.07-涅槃行动第二十天
  10. 【丐中丐】废旧光驱改装激光雕刻机
  11. Mathematica图像处理
  12. AGND DGND PGND GND
  13. 关于“程序员996”,互联网大神发话了,能顶用吗?
  14. 货币的未来取决于打破关于货币历史的虚构谎言
  15. 北京妙计旅行 后台开发实习一面二面
  16. 上海市犬伤处置门诊目录(上海哪些医院可以打狂犬疫苗)
  17. 绝密隐私!有趣的网络摄像头大揭露
  18. js对节点 属性的操作
  19. Final阶段基于spec评论作品
  20. 清理Ubuntu虚拟机磁盘空间

热门文章

  1. 解决idea的项目启动报404的问题
  2. 同事乱用 Redis 卡爆,我真是醉了
  3. 简单总结下8.25技术大会感受
  4. mysql 数据字典详解_MySQL8功能详解——数据字典
  5. 查看前端页面请求的文件地址_前端性能优化方法
  6. android 监听通知栏点击事件,android使用NotificationListenerService监听通知栏消息
  7. mysql实现主从复制的方式_mysql实现主从复制、读写分离的配置方法(二)
  8. python读取excel_Python简单读取Excel示例
  9. BZOJ2431:[HAOI2009]逆序对数列(DP,差分)
  10. 《精通LabVIEW虚拟仪器程序设计与案例实现》一2.6 VI的创建与编辑