5 深入浅出索引(下)

5 深入浅出索引(下)

 上一篇介绍了innodb索引的数据结构模型,这一篇将继续索引有关的概念

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

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

执行语句select * from T5 where k BETWEEN 3 and 5;

需要执行几次索引的搜索操作,会扫描多少行

先看索引的结构

分析sql语句的执行流程

--1 k索引树上找到k=3的记录,取得id=300

--2 再到id索引树上查到id=300对应的R3

--3 k索引上找到k=5的记录,取得id=500

--4 再回到id索引找到id=500的对应值R4

--5 k索引上取下一个值k=6,不满足条件,循环结束。

在这个过程中,回到主键索引树搜索的过程,称为回表,这个查询过程读了k索引树的3条记录,回表了两次。

在这个例子中,由于查询结果所需要的数据只在主键索引上有,所以不得不回表,那么,有没有可能经过索引优化,避免回表过程呢?

覆盖索引

执行语句

(system@127.0.0.1:3306) [test]> select id from T5 where k BETWEEN 3 and 5;

这时只需要查询id的值,而id的值已经在k索引树上,可以直接提供查询结果,不需要回表,这个查询里,索引k已经”覆盖了”我们的查询需求,称为覆盖索引

由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的优化手段

在讨论一个问题,在一个市民信箱表上,是否有必须要将身份证和名字建立联合索引?

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

现在有一个高频需求,根据市民的身份证号查询他的姓名,建立一个联合索引(身份证号、姓名),在这个查询上用到覆盖索引,不需要在回表查整行记录,减少语句的执行时间。

索引字段的维护是有代价的,因此,在建立冗余索引来支持覆盖索引时就需要权衡考虑。

最左前缀原则

B+树这种索引结构,可以利用索引的”最左前缀”

前面的(name,age)索引来分析

可以看到,索引项是按照索引定义里面出现字段的顺序排序的。

查询语句” where name like ‘%’ ”能够用上索引,查询到第一个符合条件的记录是id3,然后向后遍历,直到不满足条件为止

可以看到,不只是索引的全部定义,只要满足最左前缀,就可以利用索引来加速检索,这个最左前缀可以是联合字段索引的最左N个字段,也可以是字符串索引的最左M个字符。

基于上面对最左前缀索引的说明:在建立联合索引的时候,如何安排索引内的字段顺序

  评估标准是,索引的复用能力,因为可以支持最左前缀,所以当已经有了(a,b)这个联合索引后,一般不需要单独在a上建立索引了。第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的

  联合索引(a,b),查询条件只有b的语句,是无法使用该索引的,得新建一个(b)的索引才能使用。

索引下推

前面说到满足最左前缀原则的时候,最左前缀可以用于在索引中定位记录

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

 

Mysql 5.6引入了索引下推优化(index condition pushdown)可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数

思考题

表结构定义如下

CREATE TABLE `geek` (
  `a` int(11) NOT NULL,
  `b` int(11) NOT NULL,
  `c` int(11) NOT NULL,
  `d` int(11) NOT NULL,
  PRIMARY KEY (`a`,`b`),
  KEY `c` (`c`),
  KEY `ca` (`c`,`a`),
  KEY `cb` (`c`,`b`)
) ENGINE=InnoDB;

由于历史原因表需要ab做联合主键,cacb两个索引的查询

select * from geek where c=N order by a limit 1;

select * from geek where c=N order by b limit 1;

分析联合索引cacb和索引c之间的合理性

--添加索引c

--添加索引cb

分析

表记录

--a--|b--|c--|d

主键ab的聚簇索引组织顺序相当于order by a,b,也就是先按a排序,再按b排序,c无序

索引ca的组织是先按照c排序,再按照a排序,同时记录主键

--c--|a--|主键部分b(注意这里不是ab,而是只有b

这个跟索引c的数据是一样的

索引cb是先c排序,再按b排序,同时记录主键

--c--|b--|主键部分a--

所以,索引ca可以去掉,cb可以保留

posted @ 2019-06-21 14:51  春困秋乏夏打盹  阅读(300)  评论(0编辑  收藏  举报