优化器生成最优执行计划需要考虑的因素

MySQL有一个优化器,专门负责生成最优的查询计划,生成最优查询计划可能考虑的因素有:

  • 扫描行数
  • 是否排序
  • 是否需要回表
  • 是否需要临时表 等等

在不同的因素作用下,生成的查询计划可能和我们预想的不同。

具体实例

实验前

先准备好表

CREATE TABLE `t` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `a` (`a`),
KEY `b` (`b`)
) ENGINE=InnoDB;

使用存储过程插入10万条数据

delimiter ;;
create procedure idata()
begin
declare i int;
set i=1;
while(i<=100000)do
insert into t values(i, i, i);
set i=i+1;
end while;
end;;
delimiter ;
call idata();
1. 范围查询某普通索引字段,引擎选择了全表扫描,没有使用索引

调用下列语句:

explain select * from t  where a between 20000 and 40000;

执行计划查询结果如下:

| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| 1 | SIMPLE | t | NULL | ALL | a | NULL | NULL | NULL | 100448 | 37.37 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

发现mysql使用了全表扫描,没有使用a列上的普通索引。

原因如下:

如果使用普通索引查询,还需要回表操作。当回表次数占总数据行数达到一定比例时,做随机IO查询的效率较低,并且当磁盘是机械硬盘时,多次随机IO查询一定比顺序查询的全表扫描要慢的多。

但是如果使用的是固态硬盘,随机读操作的性能很高,可以强制或者引导MySQL优化器使用普通索引来查询。

2. 查询语句中含有order by 可能会促使mysql选择排序字段对应的索引

调用下列语句:

explain select * from t where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;

执行计划查询结果如下:

| id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows  | filtered | Extra                              |
+----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+------------------------------------+
| 1 | SIMPLE | t | NULL | range | a,b | b | 5 | NULL | 50224 | 1.00 | Using index condition; Using where |
+----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+------------------------------------+
1 row in set, 1 warning (0.00 sec)

发现MySQL选择了b列上的索引。

我们分析一下,如果使用a列上的索引,搜索的行数为1000行,回表次数为1000次,因为使用a列索引,所以排序b列时,会在排序消耗一些时间;如果使用b列的索引,搜索行数变多,回表也变多,但是不需要排序。显然,mysql在这里更加注重了排序的影响,所以选择了b列的索引。

我们执行一下强制使用a列索引的相同sql语句,和不使用force index的sql语句都执行三次,对比一下执行时间,可以查看到如果使用a索引,平均执行时间要比使用b列索引快的多。

mysql> select * from t where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;
Empty set (0.05 sec) mysql> select * from t where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;
Empty set (0.04 sec) mysql> select * from t where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;
Empty set (0.05 sec) mysql> select * from t force index(a) where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;
Empty set (0.01 sec) mysql> select * from t force index(a) where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;
Empty set (0.00 sec) mysql> select * from t force index(a) where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;
Empty set (0.00 sec)
总结

相同的sql语句,在不同的计算机下,执行时间也会变得不同,上面的实验结果只代表在作者的计算机上运行得到的结果,在进行sql调优时,要根据当时环境的实际执行时间进行调试,然后决定是否应该强制使用索引。


注:这一节中,最后会写一些关于字符串加索引的思考,因为这部分知识不足以构成一个小节,所以把它添加到了这里。

字符串加索引的方式

给字符串字段加索引有几种方式:

  1. 直接创建字符串字段的完整索引,支持范围和等值查询
  2. 创建前缀索引,可能会增加扫描行数,会导致覆盖索引失效
  3. 如果前缀区分度不高,可以使用倒序存储,再根据倒叙存储的字段创建前缀索引
  4. 通过加入一个新的字段,这个字段的值为hash计算过的字段值,有额外的计算和存储消耗

第234种方式,考虑的更多的是节省存储空间,但是都增加了维护的成本。比如:

  • 第三种方式,存储的时候就需要业务或者sql保证倒叙存储,查询的时候也需要相应的利用业务或者sql倒叙函数查询,如果一旦在业务或者sql语句上忘记使用倒叙,那么在实际存储的时候也不会报错,但是会影响业务。
  • 第四种方式,除了有索引的消耗以外,还多了一个存储字段,如果有多个字符串字段需要设计搜因,那么需要增加N个hash索引字段。另外,存储和查询的时候,也多了hash计算的消耗。

在目前硬件越来越便宜的趋势下,直接使用1或者2方式创建完整或者前缀索引是完全可以的,2虽然会增加扫描行数和回表成本,但在目前的硬件下这些损耗几乎是可以忽略不计。

MySQL-SQL调优-引擎选错索引或者不使用索引分析 和 字符串加索引的方式思考的更多相关文章

  1. 《高性能SQL调优精要与案例解析》一书谈主流关系库SQL调优(SQL TUNING或SQL优化)核心机制之——索引(index)

    继<高性能SQL调优精要与案例解析>一书谈SQL调优(SQL TUNING或SQL优化),我们今天就谈谈各主流关系库中,占据SQL调优技术和工作半壁江山的.最重要的核心机制之一——索引(i ...

  2. Oracle SQL调优记录

    目录 一.前言 二.注意点 三.Oracle执行计划 四.调优记录 @ 一.前言 本博客只记录工作中的一次oracle sql调优记录,因为数据量过多导致的查询缓慢,一方面是因为业务太过繁杂,关联了太 ...

  3. Oracle SQL调优之分区表

    目录 一.分区表简介 二.分区表优势 三.分区表分类 3.1 范围分区 3.2 列表分区 3.3 散列分区 3.4 组合分区 四.分区相关操作 五.分区相关查询 附录:分区表索引失效的操作 一.分区表 ...

  4. MySQL索引和SQL调优手册

    MySQL索引 MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BTree索引,哈希索引,全文索引等等.为了避免混乱,本文将只关注于BTree ...

  5. 你们一般都是怎么进行SQL调优的?MySQL在执行时是如何选择索引的?

    前言 过年回来的第二周了,终于有时间继续总结知识了.这次来看一下SQL调优的知识,这类问题基本上面试的时候都会被问到,无论你的岗位是后端,运维,测试等等. 像本文标题中的两个问题,就是我在实际面试过程 ...

  6. MySql(十一):MySQL性能调优——常用存储引擎优化

    一.前言 MySQL 提供的非常丰富的存储引擎种类供大家选择,有多种选择固然是好事,但是需要我们理解掌握的知识也会增加很多.本章将介绍最为常用的两种存储引擎进行针对性的优化建议. 二.MyISAM存储 ...

  7. MySQL性能调优与架构设计——第11章 常用存储引擎优化

    第11章 常用存储引擎优化 前言: MySQL 提供的非常丰富的存储引擎种类供大家选择,有多种选择固然是好事,但是需要我们理解掌握的知识也会增加很多.每一种存储引擎都有各自的特长,也都存在一定的短处. ...

  8. MySQL性能调优与架构设计——第3章 MySQL存储引擎简介

    第3章 MySQL存储引擎简介 3.1 MySQL 存储引擎概述 MyISAM存储引擎是MySQL默认的存储引擎,也是目前MySQL使用最为广泛的存储引擎之一.他的前身就是我们在MySQL发展历程中所 ...

  9. MySQL 性能调优之存储引擎

    原文:http://bbs.landingbj.com/t-0-246222-1.html        http://bbs.landingbj.com/t-0-245851-1.html MySQ ...

  10. 【叶问】 MySQL常用的sql调优手段或工具有哪些

     MySQL常用的sql调优手段或工具有哪些1.根据执行计划优化   通常使用desc或explain,另外可以添加format=json来输出更详细的json格式的执行计划,主要注意点如下:     ...

随机推荐

  1. .NetCore依赖注入(DI)之生命周期

    在 .NET Core 中,依赖注入(Dependency Injection,DI)是一种实现控制反转(Inversion of Control,IoC)的技术,它通过将依赖对象注入到需要它们的对象 ...

  2. Diary & Note - 两个惊喜

      我们有单位根反演: \[\sum_{k\mid n}[x^n]f(x)=\frac{1}{k}\sum_{i=0}^{k-1}f(\omega_k^i). \] 我们有 CRT: \[x\equi ...

  3. MyBatis中的 10 个宝藏技巧!

    前言 说到 MyBatis,很多小伙伴都会用,但未必用得"惊艳". 实际上,这个轻量级的持久层框架还有很多隐藏的"宝藏技巧". 如果你能掌握这些技巧,不但能让开 ...

  4. Android开发快速入门iOS开发概览

    注:本文同步发布于微信公众号:stringwu的互联网杂谈 Android开发快速入门iOS开发概览 1 前言 笔者总结了自己在拥有Android开发的相关基础后入门iOS开发时遇到的点点滴滴给其他想 ...

  5. java多线程与线程池-copy

    1. 场景描述 以前多线程也常用,这次因需再页面上用到多线程,如下图,总结下,有需要的朋友可以参考下. 2. 解决方案 2.1 线程池概念 线程池官方定义不说了,通俗说下:池子的概念,事先(预定义)创 ...

  6. Golang-结构体6

    http://c.biancheng.net/golang/struct/ Go语言结构体定义 Go语言可以通过自定义的方式形成新的类型,结构体就是这些类型中的一种复合类型,结构体是由零个或多个任意类 ...

  7. 学Shiro完结版-2

    第四章 INI配置--<跟我学Shiro> 之前章节我们已经接触过一些INI配置规则了,如果大家使用过如Spring之类的IoC/DI容器的话,Shiro提供的INI配置也是非常类似的,即 ...

  8. 未能加载文件或程序集"Symtem.Data.SQLite.dll"或它的某一个依赖项

    我在window service 2016 等更高版本的服务器上遇见过,普通window系统未预见.可尝试安装 VC ++ 2010 我下载安装  之后就好了.

  9. TCP/IP协议栈封装解封装过程

    发送方将用户数据提交给应用程序把数据送达目的地,整个数据封装流程如下: 用户数据首先传送至应用层,添加应用层信息: 完成应用层处理后,数据将往下层传输层继续传送,添加传输层信息(如TCP或UDP,应用 ...

  10. Linux iostat 命令详解

    Linux iostat 命令详解 在Linux系统管理中,监控磁盘I/O性能是一项至关重要的任务.iostat是sysstat包中的一个实用工具,用于监控和显示系统输入输出设备和CPU的使用情况.它 ...