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

1 Condition Filtering

join中,prefix rows指从一个table中扫描出来的传递给下一个表的行,为了防止传递的行增长的过快,有时候,优化器会尝试提前对prefix rows进行过滤(比如将where中的条件提前等)。

有助于进行此操作的条件需要符合如下场景:

  • 该过滤条件作用的对象是当前表
  • 依赖于join序列中前面表的值(常量或者普通的值)
  • 不会因为access method(这里指join)而导致结果改变。

EXPLAIN的输出结果里面,rows列表示优化器为access method预估的行数,filtered列表示condition filtering所带来的效果,其值为一个百分比,100%表示没有列被过滤,随之递减,表示过滤的效果。

优化器为prefix rows预估的量(称为prefix rows count)即为rows列乘以filtered

来看一个例子

SELECT *
FROM employee JOIN department ON employee.dept_no = department.dept_no
WHERE employee.first_name = 'John'
AND employee.hire_date BETWEEN '2018-01-01' AND '2018-06-01';

假设这两个表信息如下:

  • employee有1024行, department有12行

  • 两张表在dept_no列上都有索引,且employee表在first_name也有索引

  • employee中有8行满足employee.first_name = 'John'

  • employee有150行满足employee.hire_date BETWEEN '2018-01-01' AND '2018-06-01'

  • employee中只有1行满足employee.first_name = 'John' AND employee.hire_date BETWEEN '2018-01-01' AND '2018-06-01';

如果没有condition filtering,那么EXPLAIN的输出结果如下:

+----+------------+--------+------------------+---------+---------+------+----------+

| id | table | type | possible_keys | key | ref | rows | filtered |

+----+------------+--------+------------------+---------+---------+------+----------+

| 1 | employee | ref | name,h_date,dept | name | const | 8 | 100.00 |

| 1 | department | eq_ref | PRIMARY | PRIMARY | dept_no | 1 | 100.00 |

+----+------------+--------+------------------+---------+---------+------+----------+

可以看到,在employee中符合employee.first_name = 'John'的有8行,因此没有filtering被应用,此时的prefix row count8 * 100% = 8

当应用condition filtering的时候,优化器就会应用where语句中没有被access method(此处为join)所采用的过滤条件,在这个例子里面,这个条件是BETWEEN语句,因此,此时EXPLAIN的输出结果为:

+----+------------+--------+------------------+---------+---------+------+----------+

| id | table | type | possible_keys | key | ref | rows | filtered |

+----+------------+--------+------------------+---------+---------+------+----------+

| 1 | employee | ref | name,h_date,dept | name | const | 8 | 16.31 |

| 1 | department | eq_ref | PRIMARY | PRIMARY | dept_no | 1 | 100.00 |

+----+------------+--------+------------------+---------+---------+------+----------+

此时的prefix rows count8 * 16.31% = 1.3,已经很接近实际的数据量了,此时传给department的数据量已经只有一行了。

通常来讲,优化器不会为最后一个表进行condition filtering预估,因为最后一张表已经不会把数据往下传了,因此,没有必要这样做。

下面的做法会有助于优化器使用这项技术:

  • 如果某一列没有加索引,那么对其创建索引,这样子优化器就可以有足够的信息对其进行row estimates

  • 类似的,如果某一列没有直方图,那么生成一个

  • 修改join顺序

  • 禁止对session的condition filtering:

    SET optimizer_switch = 'condition_fanout_filter=off';

    或者使用Mysql的hint功能:

    SELECT /*+ SET_VAR(optimizer_switch = 'condition_fanout_filter=off') */ ...

2 Constant-Folding Optimization

在平时的使用过程中,如果用户在where语句中进行范围判断,判断的常量值超过列类型的范围,那么,此时where条件将会被折叠,比如下面的例子:

CREATE TABLE t (c TINYINT UNSIGNED NOT NULL);

如果用户使用如下的查询语句:

SELECT * FROM t WHERE c < 256

由于c的类型为TINYINT UNSIGNED,那么它的最大值就会255,此时,Mysql会把where条件优化为where 1,相应的,如果在定义c的时候没有NOT NULL,那么where条件会被优化为 where c IS NOT NULL.

相应的,Mysql在处理浮点型的时候,也会做对应的处理,如果用户指定的常量值超过了精度范围,那么会做适当的裁剪。

3 ORDER BY Optimization

在进行order by操作时,Mysql主要使用两种方式来进行排序,如果索引可以使用,那么直接使用索引自身的排序,如果不能,则使用filesort来进行排序。

  • Use of Indexes to Satisfy ORDER BY

    当用户进行查询的时候,如果查询的列多于order by的列(order by的列有索引),那么此时也不一定会使用索引进行排序,因为用户选择的列多于索引列,Mysql就需要通过索引去盘上读其他列,此时带来的开销有可能会大于直接扫描表在进行filesort的开销。

    下面的例子,Mysql均有可能会使用索引来进行排序:

    • 示例1:
    SELECT pk, key_part1, key_part2 FROM t1
    ORDER BY key_part1, key_part2;

    key_part1key_part2均建有索引,并且查询的结果也全都为索引,在Innodb中,primary key默认为索引的一部分。

    • 示例2:
    SELECT * FROM t1
    ORDER BY key_part1 DESC, key_part2 DESC; SELECT * FROM t1
    WHERE key_part1 = constant
    ORDER BY key_part2 DESC;

    key_part1等于一个constant,相当于当前的排序完全靠key_part2来决定,此时Mysql也可能使用索引来进行排序。

    • 示例3:
    SELECT * FROM t1
    ORDER BY key_part1 DESC, key_part2 ASC;

    即使两个相反,也有可能使用索引来进行排序。

    • 示例4:
    SELECT * FROM t1
    WHERE key_part1 > constant
    ORDER BY key_part1 ASC; SELECT * FROM t1
    WHERE key_part1 < constant
    ORDER BY key_part1 DESC;

    只要是和常量进行比较,就有可能使用索引来进行排序。

    下面的例子,即使order by使用了索引,但是Mysql也没办法使用索引来排序

    • order by使用了不同的索引:

      SELECT * FROM t1 ORDER BY key1, key2;

      key1,key2指两个不同的索引,而并非一个索引的两个部分。

    • order by的对象是非连续的索引:

      SELECT * FROM t1 WHERE key2=constant ORDER BY key1_part1, key1_part3;
    • 用来获取rows的索引不同于order by中的索引:

    SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
    • order by的对象虽然是索引,但是不是索引列,而是一个针对于索引的表达式:

      SELECT * FROM t1 ORDER BY ABS(key);
      SELECT * FROM t1 ORDER BY -key;
    • join了很多个table,但是order by的列并不是完全来自于第一个非const table

    • order by的对象和group by的对象不一样。

    • 只索引了列的一部分,比如char(20),结果只索引了前10个字节,那么此时也没办法使用索引来进行排序。

    • 索引没有按照顺序来存储,比如HASH INDEX

    在Mysql5.7或者之前的版本里,group by在特定条件下会包含默认的排序动作,所以需要在末尾加上order by NULL来避免这种排序动作,但是在Mysql8.0往后,则不再需要这样的写法,这种默认的排序动作已经被取消,同样的,产生的结果顺序也将和以前的版本会有些不同,为了保证顺序,请加上order by这样的语句。

  • Use of filesort to Satisfy ORDER BY

    在Mysql8.0.12之前, sort_buffer_size参数会分配一个固定的大小的内存来进行filesort操作,在8.0.12之后,这种方式被优化为根据需求增量的使用内存,这样子可以更高效的利用内存,从而尽可能避免的使用临时文件来进行排序。

  • Influencing ORDER BY Optimization

    对于那些没有使用filesort的排序,试着减小 max_length_for_sort_data 环境变量,该值设置过高会导致较高的磁盘读写和较低CPU运算。如果一个排序无法使用索引,尝试使用下面的策略来优化filesort。

    • 增大sort_buffer_size ,可以避免更少的磁盘读写和合并操作,同时需要注意的是,如果增加了 max_sort_length,那么同样的也要增加sort_buffer_size
    • 增加 read_rnd_buffer_size ,这样子可以使得一次读出更多的行数
    • tmpdir 系统变量修改到更大的磁盘上,生成的临时文件将会有足够的存储空间
  • ORDER BY Execution Plan Information Available

    EXPLAIN的输出里面,对于使用index排序和filesort排序分别有以下两种输出:

    • 如果Extra列没有包含Using filesort,那么使用index排序
    • 如果Extra包含Using filesort,那么使用的filesort进行的排序。

    此外,如果使用的是filesort,那么optimizer trace的输出会有一个filesort_summary块,举例如下:

    "filesort_summary": {
    "rows": 100,
    "examined_rows": 100,
    "number_of_tmp_files": 0,
    "peak_memory_used": 25192,
    "sort_mode": "<sort_key, packed_additional_fields>"
    }

    部分解释如下:

    peak_memory_used:表示排序过程中使用的最大内存,由于Mysql8.0.12后,filesort使用的内存是增量增加的,而不再是以前固定好一次性申请,因此,该值就会随着实际情况而变化;

    sort_mode解释如下:

    • <sort_key, rowid>:表示排序是按照sort key进行的,rowid用来从表中读取具体的行
    • <sort_key, additional_fields>: additional_fields为sql语句需要查询的列,sort key用来排序,additional_fields的值直接从表中读取
    • <sort_key, packed_additional_fields>:和上一个类似,但是additional_fields是被打包在一起,而非用一种固定长度的编码。

Mysql优化(出自官方文档) - 第四篇的更多相关文章

  1. Mysql优化(出自官方文档) - 第九篇(优化数据库结构篇)

    目录 Mysql优化(出自官方文档) - 第九篇(优化数据库结构篇) 1 Optimizing Data Size 2 Optimizing MySQL Data Types 3 Optimizing ...

  2. Mysql优化(出自官方文档) - 第二篇

    Mysql优化(出自官方文档) - 第二篇 目录 Mysql优化(出自官方文档) - 第二篇 1 关于Nested Loop Join的相关知识 1.1 相关概念和算法 1.2 一些优化 1 关于Ne ...

  3. Mysql优化(出自官方文档) - 第一篇(SQL优化系列)

    Mysql优化(出自官方文档) - 第一篇 目录 Mysql优化(出自官方文档) - 第一篇 1 WHERE Clause Optimization 2 Range Optimization Skip ...

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

    Mysql优化(出自官方文档) - 第六篇 目录 Mysql优化(出自官方文档) - 第六篇 Optimizing Subqueries, Derived Tables, View Reference ...

  5. Mysql优化(出自官方文档) - 第三篇

    目录 Mysql优化(出自官方文档) - 第三篇 1 Multi-Range Read Optimization(MRR) 2 Block Nested-Loop(BNL) and Batched K ...

  6. Mysql优化(出自官方文档) - 第五篇

    目录 Mysql优化(出自官方文档) - 第五篇 1 GROUP BY Optimization 2 DISTINCT Optimization 3 LIMIT Query Optimization ...

  7. Mysql优化(出自官方文档) - 第八篇(索引优化系列)

    目录 Mysql优化(出自官方文档) - 第八篇(索引优化系列) Optimization and Indexes 1 Foreign Key Optimization 2 Column Indexe ...

  8. Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇)

    Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇) 目录 Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇) 1 Internal Locking Methods Row-Leve ...

  9. Mysql优化(出自官方文档) - 第十篇(优化InnoDB表篇)

    Mysql优化(出自官方文档) - 第十篇(优化InnoDB表篇) 目录 Mysql优化(出自官方文档) - 第十篇(优化InnoDB表篇) 1 Optimizing Storage Layout f ...

随机推荐

  1. SQL Server 查看进程阻塞及处理

    修改或删除数据前先备份,先备份,先备份(重要事情说三遍)! 1.首先,查看线程,分析是否存在阻塞进程,blocked>0都是当前被阻塞的进程  SELECT * FROM sysprocesse ...

  2. 最优运输(Optimal Transfort):从理论到填补的应用

    目录 引言 1 背景 2 什么是最优运输? 3 基本概念 3.1 离散测度 (Discrete measures) 3.2 蒙日(Monge)问题 3.3 Kantorovich Relaxation ...

  3. JVM虚拟机 类加载过程与类加载器

    目录 前言 类的生命周期 类加载过程 加载 连接 验证 准备 解析 初始化 类加载器 三大类加载器 双亲委派模型 概念 为什么要使用双亲委派模型 源码分析 反双亲委派模型 参考 前言 类装载器子系统是 ...

  4. Django(31)模板中常用的过滤器

    模版常用过滤器 在模版中,有时候需要对一些数据进行处理以后才能使用.一般在Python中我们是通过函数的形式来完成的.而在模版中,则是通过过滤器来实现的.过滤器使用的是|来使用. add 将传进来的参 ...

  5. 并发王者课 - 青铜 2:峡谷笔记 - 简单认识Java中的线程

    在前面的<兵分三路:如何创建多线程>文章中,我们已经通过Thread和Runnable直观地了解如何在Java中创建一个线程,相信你已经有了一定的体感.在本篇文章中,我们将基于前面的示例代 ...

  6. 前端的MySQL基础

    前端MySQL 一.引言 MySQL是一个关系型数据库管理系统,在Web应用方面,MySQL是最好的应用之一.其主要的他点是体积小.速度块.总体成本低.源码开放 二.MySQL的构成 在我们开始学习M ...

  7. [BD] Flume

    什么是Flume 采集日志,存在HDFS上 分布式.高可用.高可靠的海量日志采集.聚合和传输系统 支持在日志系统中定制各类数据发送方,用于收集数据 支持对数据进行简单处理,写到数据接收方 组件 sou ...

  8. MegaCli是一款管理维护硬件RAID软件,可以通过它来了解当前raid卡的所有信息,包括 raid卡的型号,raid的阵列类型,raid 上各磁盘状态

    MegaCli 监控raid状态 转载weixin_30344131 最后发布于2015-10-16 13:05:00 阅读数    简介 MegaCli是一款管理维护硬件RAID软件,可以通过它来了 ...

  9. C语言printf-(转自shiney)

    1.调用格式为  printf("<格式化字符串>", <参量表>);   其中格式化字符串包括两部分内容: 一部分是正常字符, 这些字符将按原样输出; 另 ...

  10. Java中单列集合List排序的真实应用场景

    一.需求描述 最近产品应客户要求提出了一个新的需求,有一个列表查询需要按照其中的多列进行排序. 二.需求分析 由于数据总量不多,可以全部查询出来,因此我就考虑使用集合工具类Collections.so ...