MySQL实战45讲学习笔记:第五讲

1、须要回表的案例

在下面表T中,执行下面语句,须要执行几回树的搜索操做?会扫描多少行?mysql

select * from T where k between 3 and 5

一、初始化语句

mysql> create table T (
ID int primary key,
k int NOT NULL DEFAULT 0, 
s varchar(16) NOT NULL DEFAULT '',
index k(k))
engine=InnoDB;

insert into T values(100,1, 'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg');

二、这条SQL语句的执行流程

一、在 k 索引树上找到 k=3 的记录,取得 ID = 300...
二、再到 ID 索引树查到 ID=300 对应的 R3;
三、在 k 索引树取下一个值 k=5,取得 ID=500;
四、再回到 ID 索引树查到 ID=500 对应的 R4;
五、在 k 索引树取下一个值 k=6,不知足条件,循环结束。sql

这个过程当中回到主键索引树搜索的过程,咱们称为回表,能够看到这个查询过程读了K索引树的3条记录
(步骤一、3和5),回表了两次(步骤一、3和5)性能优化

2、如何避免回表

在上面的例子中,因为查询结果锁须要的数据只在主键索引上有,因此不得不回表,
那么。有没有可能通过索引优化,避免回表过程?架构

select ID from T where k between 3 and 5

一、覆盖索引

索引k已经"覆盖了"咱们的查询需求。咱们称为覆盖索引性能

因为覆盖索引能够减小树的搜索次数,显著提高查询性能,因此使用覆盖索引是一个经常使用的性能优化手段优化

二、在一个市民信息表上,是否有必要将身份证号和名字创建联合索引

假设这个市民表的定义是这样的:spa

CREATE TABLE `tuser` (
  `id` int(11) NOT NULL,
  `id_card` varchar(32) DEFAULT NULL,
  `name` varchar(32) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `ismale` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `id_card` (`id_card`),
  KEY `name_age` (`name`,`age`)
) ENGINE=InnoDB

若是如今有一个高频请求,要根据市民的身份证号查询他的姓名,这个联合索引就有意义了,
它能够在这个高频请求上用覆盖索引、再也不须要回表查整行记录,减小语句的执行时间3d

三、回表的缺点

固然、索引字段的维护老是有代价的,创建冗余索引来支持覆盖索引时就须要权衡考虑了,这正是业务DBA,或者称为业务数据架构师的工做blog

3、最左前缀原则

一、疑问

单独为一个不频繁的请求建立一个索引又感受有点浪费,应该怎么作呢?索引

二、解决方案

B+树这种索引结构,能够利用索引的"最左前缀",来定位记录

 

一、为了直观地说明这个概念、咱们用(name、age)这个联合索引来分析

一、查到全部名字是“张三”的人

快速定位到ID4,而后向后遍历获得全部须要的结果

二、你要查的是全部名字第一个字是“张”的人

where name like '张%'

查找到第一个符合条件的记录是ID3,而后向后遍历,知道不知足条件为止。
你要查的是全部名字第一个字是“张”的人

不仅是索引的所有定义,只要知足最左前缀,就能够可利用索引来加速检索、这个最左前缀能够是联合索引的最左N个字段,也能够是字符串索引的最左M个字符

三、在创建联合索引的时候,如何安排索引内的字段顺序

一、评估标准

索引的复用能力,

由于能够支持最左前缀,因此已经有了(a,b)这两个联合所用后,通常就不须要单独在b上面创建索引了

二、第一原则是

若是经过调整顺序,能够减小维护一个索引。那么这个顺序每每就是须要优先考虑采用的

三、既有联合查询,又有基于 a、b 各自的查询呢?

同时维护(a,b)(b)这两个索引、这时候咱们要考虑的原则就是空间了。

好比上面这个市民表的状况、name字段是比age字段大的,那我就建议你建立一个(name,age)的联合索引和一个(age)的字段索引

4、索引下推

 咱们仍是以市民表的联合索引(name, age)为例。检索出表中:"名字第一个字是长,而年龄是10岁的全部男孩" 

一、名字第一个字是长,而年龄是10岁的全部男孩

一、SQL语句是这么写的

mysql> select * from tuser where name like '张 %' and age=10 and ismale=1;

一、 找到第一个知足条件的记录ID3(这还不错,总比权标扫描要好)
二、判断其余是否知足条件

三、无索引执行流程

5.6以前只能从ID3开始一个一个回表,到主键索引上找出数据航,再对比字段值不去判断age

执行流程

每个虚线箭头表示回表一次

一、InnoDB 并不会去看 age的值,
二、只是按顺序把name的第一个子是'张'的记录一条取出来回表,所以须要回表4次

四、索引下推执行流程

5.6以后引入的索引下推优化,能够在索引遍历过程当中对索引中包含的字段先判断,直接过滤掉不知足条件的记录,减小回表次数
内部就判断了age是否等于10

执行流程

每个虚线箭头表示回表一次

一、InnoDB 内部就哦按段了age是否等于10,
二、对不等于10的记录,直接判断跳过,在咱们这个例子中只须要对ID四、ID4回表2次

5、联合索引的技巧

一、覆盖索引

若是查询条件使用的是普通索引(或是联合索引的最左原则字段),查询结果是联合索引的字段或是主键,不用回表操做,直接返回结果,减小IO磁盘读写读取正行数据

二、最左前缀

联合索引的最左 N 个字段,也能够是字符串索引的最左 M 个字符

三、联合索引

根据建立联合索引的顺序,以最左原则进行where检索,好比(age,name)以age=1 或 age= 1 and name=‘张三’可使用索引,单以name=‘张三’ 不会使用索引,考虑到存储空间的问题,还请根据业务需求,将查找频繁的数据进行靠左建立索引。

四、索引下推

like 'hello%’and age >10 检索,MySQL5.6版本以前,会对匹配的数据进行回表查询。5.6版本后,会先过滤掉age<10的数据,再进行回表查询,减小回表率,提高检索速度

相关文章
相关标签/搜索