汇集索引、辅助索引、覆盖索引、联合索引

《MySQL技术内幕 InnoDB存储引擎》学习笔记web


汇集索引(Clustered Index)

汇集索引就是按照每张表的主键构造一棵B+树,同时叶子节点中存放的即为整张表的行记录数据。sql

举个例子,直观感觉下汇集索引。svg

建立表t,并以人为的方式让每一个页只能存放两个行记录(不清楚怎么人为控制每页只存放两个行记录):工具

这里写图片描述学习

最后《MySQL技术内幕》的做者经过分析工具获得这棵汇集索引树的大体构造以下:
这里写图片描述优化

汇集索引的叶子节点称为数据页,每一个数据页经过一个双向链表来进行连接,并且数据页按照主键的顺序进行排列。spa

如图所示,每一个数据页上存放的是完整的行记录,而在非数据页的索引页中,存放的仅仅是键值及指向数据页的偏移量,而不是一个完整的行记录。3d

若是定义了主键,InnoDB会自动使用主键来建立汇集索引。若是没有定义主键,InnoDB会选择一个惟一的非空索引代替主键。若是没有惟一的非空索引,InnoDB会隐式定义一个主键来做为汇集索引。code


辅助索引(Secondary Index)

辅助索引,也叫非汇集索引。和汇集索引相比,叶子节点中并不包含行记录的所有数据。叶子节点除了包含键值之外,每一个叶子节点的索引行还包含了一个书签(bookmark),该书签用来告诉InnoDB哪里能够找到与索引相对应的行数据。xml

仍是以《MySQL技术内幕》中的例子,来直观感觉下辅助索引的模样。

仍是以上面的表t为例,在列c上建立非汇集索引:

这里写图片描述

而后做者经过分析工做获得辅助索引和汇集索引的关系图:

这里写图片描述

能够看到辅助索引idx_c的叶子节点中包含了列c的值和主键的值。

以Key为7fffffff为例,7是0111,0表明负数,真实的值应该取反加1,是-1,这是列c的值。Pointer是80000001,8是1000,1表明正数,因此80000001表明1,是主键的值。


覆盖索引(Covering index)

InnoDB存储引擎支持覆盖索引,即从辅助索引中就能够获得查询的记录,而不须要查询汇集索引中的记录。

使用覆盖索引有啥好处?

  • 能够减小大量的IO操做

上图中咱们知道,若是要查询辅助索引中不含有的字段,得先遍历辅助索引,再遍历汇集索引,而若是要查询的字段值在辅助索引上就有,就不用再查汇集索引了,这显然会减小IO操做。

好比上图中,如下sql能够直接使用辅助索引,

select a from where c = -2;
  • 有助于统计

    假设存在以下表:

CREATE TABLE `student` ( `id` bigint(20) NOT NULL, `name` varchar(255) NOT NULL, `age` varchar(255) NOT NULL, `school` varchar(255) NOT NULL, PRIMARY KEY (`id`), KEY `idx_name` (`name`), KEY `idx_school_age` (`school`,`age`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

若是在该表上执行:

select count(*) from student

优化器会怎么处理?

遍历汇集索引和辅助索引均可以统计出结果,但辅助索引要远小于汇集索引,因此优化器会选择辅助索引来统计。执行explain命令:

这里写图片描述

key和Extra显示使用了idx_name这个辅助索引。

还有,假设执行如下sql:

select * from student where age > 10 and age < 15

由于联合索引idx_school_age的字段顺序是先school再age,按照age作条件查询,一般不走索引:
这里写图片描述

可是,若是保持条件不变,查询全部字段改成查询条目数:

select count(*) from student where age > 10 and age < 15

优化器会选择这个联合索引:
这里写图片描述


联合索引

联合索引是指对表上的多个列进行索引。

如下为建立联合索引idx_a_b的示例:

这里写图片描述

联合索引的内部结构:

这里写图片描述

联合索引也是一棵B+树,其键值数量大于等于2。键值都是排序的,经过叶子节点能够逻辑上顺序的读出全部数据。数据(1,1)(1,2)(2,1)(2,4)(3,1)(3,2)是按照(a,b)先比较a再比较b的顺序排列。

基于上面的结构,对于如下查询显然是可使用(a,b)这个联合索引的:

select * from table where a=xxx and b=xxx ;

select * from table where a=xxx;

可是对于下面的sql是不能使用这个联合索引的,由于叶子节点的b值,1,2,1,4,1,2显然不是排序的。

select * from table where b=xxx

联合索引的第二个好处是对第二个键值已经作了排序。举个例子:

create table buy_log( userid int not null, buy_date DATE )ENGINE=InnoDB;

insert into buy_log values(1, '2009-01-01');
insert into buy_log values(2, '2009-02-01');

alter table buy_log add key(userid);
alter table buy_log add key(userid, buy_date);

当执行

select * from buy_log where user_id = 2

时,优化器会选择key(userid);可是当执行如下sql:

select * from buy_log where user_id = 2 order by buy_date desc;

时,优化器会选择key(userid, buy_date),由于buy_date是在userid排序的基础上作的排序。

若是把key(userid,buy_date)删除掉,再执行:

select * from buy_log where user_id = 2 order by buy_date desc;

优化器会选择key(userid),可是对查询出来的结果会进行一次filesort,即按照buy_date从新排下序。因此联合索引的好处在于能够避免filesort排序。