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. prometheus nginx-module-vts删除内存区数据

    项目地址:https://github.com/vozlt/nginx-module-vts 删除所zone内存中的数据 curl localhost/status/control?cmd=delet ...

  2. [bug] Authentication failed for token submission (认证失败)异常

    原因 gitee上下的项目,启动后能访问首页,但登录报错.原因是根据用户名上数据库查密码没有得到结果,中间任何环节有问题都可能导致,我的是因为mapper.xml中的<mapper namesp ...

  3. Bash技巧:使用 set 内置命令帮助调试 shell 脚本

    Bash技巧:使用 set 内置命令帮助调试 shell 脚本 霜鱼片发布于 2020-02-03   在 bash 中,可以使用 set 内置命令设置和查看 shell 的属性.这些属性会影响 sh ...

  4. Win10屏幕亮度不能调节,调节无效怎么办?

    Win10屏幕亮度不能调节,调节无效怎么办? 听语音 浏览:1027 | 更新:2019-11-22 11:43 1 2 3 4 5 6 7 分步阅读 一些用户在使用win10系统之后,出现了电脑屏幕 ...

  5. Linux创建RAID10_实战

    Linux创建RAID10_实战 Linux创建RAID10 RAID10 是先将数据进行镜像操作,然后再对数据进行分组,RAID1 在这里就是一个冗余的备份阵列,而RAID0则负责数据的读写阵列 至 ...

  6. 基于多端口的Web服务

    [Centos7.4版本] !!!测试环境我们首关闭防火墙和selinux [root@localhost ~]# systemctl stop firewalld [root@localhost ~ ...

  7. Cron 定时任务命令-配置文件详解

    定时任务 定时任务的作用 1.系统级别的定时任务 ​ 临时文件清理 ​ 系统信息采集 ​ 日志文件切割 2.用户级别的定时任务 ​ 定时向互联网同步时间 ​ 定时备份系统配置文件 ​ 定时备份数据库的 ...

  8. Qt 搜索框

    一.前言 用户需要输入文本时,可使用QLineEdit控件进行编辑输入,缺点是样式相对单一. 在使用百度搜索输入框时,发觉比较人性化,故采用QLineEdt+QPushButton通过css样式实现自 ...

  9. LIN总线多从机与主机通信(控制+反馈)

    概念 首先要明确以下两点: LIN 总线通信方式为主从机通信,属于异步通信 "帧头 + 数据帧"才算一完整报文 设计方案 多从机与主机通信数据流(控制+反馈)如下: 一号从机: 第 ...

  10. GO学习-(33) Go实现日志收集系统2

    Go实现日志收集系统2   一篇文章主要是关于整体架构以及用到的软件的一些介绍,这一篇文章是对各个软件的使用介绍,当然这里主要是关于架构中我们agent的实现用到的内容 关于zookeeper+kaf ...