目录
什么是索引主键索引和非主键索引什么是回表联合索引联合索引的存储结构联合索引的查询流程覆盖索引什么是覆盖索引不用主键索引就一定需要回表吗为什么使用覆盖索引就可以避免回表什么是索引
索引:是为了快速对数据库数据检索的一种数据结构,是B+ Tree
的数据结构。B+ Tree
长什么样?那你得先明白什么是B- Tree
,来看如下一张图
左边是B- Tree
,右边是B+ Tree
,两者的区别在于
B- Tree
中,所有节点都会带有指向具体记录的指针;B+ Tree
中只有叶子节点会带有指向具体记录的指针B- Tree
中不同的叶子节点之间没有连在一起;B+ Tree
中所有的叶子节点通过指针连接在一起B- Tree
中可能在非叶子节点就拿到了指向具体记录的指针,搜索效率不稳定;B+ Tree
中一定要到叶子节点中才可以获取到具体记录的指针,搜索效率稳定
基于上面两点分析,我们可以得出如下结论
B+ Tree
中,由于非叶子节点不带有指向具体记录的指针,所以非叶子节点中可以存储更多的索引项,这样就可以有效降低树的高度,进而提高搜索的效率B+ Tree
中,叶子节点通过指针连接在一起,这样如果有范围查找的需求,那么实现起来将非常容易,而对于B- Tree
,范围查找则需要不停的在叶子节点和非叶子节点之间移动
主键索引和非主键索引
大家知道,mysql
中的索引有很多中不同的分类方式,可以按照数据结构分,可以按照逻辑角度分,也可以按照物理存储分。其中,按照物理存储方式,可以分为聚簇索引和非聚簇索引
我们日常所说的主键索引,其实就是聚簇索引,主键索引之外,其他的都称之为非主键索引,非主键索引也被称为二级索引,或者叫作辅助索引
对于主键索引和非主键索引,使用的数据结构都是B+ Tree
,唯一的区别在于叶子节点中存储的内容不同
主键索引的叶子节点存储的是一行完整的数据库数据非主键索引的叶子节点存储的则是主键值
所以,当我们需要查询的时候
如果是通过主键索引来查询数据,例如select * from user where id=100
,那么此时只需要搜索主键索引的B+ Tree
就可以找到数据如果是通过非主键索引来查询数据,例如select * from user where username='javaboy'
,那么此时需要先搜索username
这一列索引的B+ Tree
,搜索完成后得到主键的值,然后再去搜索主键索引的B+ Tree
,就可以获取到一行完整的数据
什么是回表
对于第二种查询方式而言,一共搜索了两棵B+ Tree
,第一次搜索非主键索引的B+ Tree
拿到主键值后,再去搜索主键索引的B+ Tree
,这个过程就是所谓的回表
从上面的分析中我们也能看出,通过非主键索引查询要扫描两棵B+ Tree
,而通过主键索引查询只需要扫描一棵B+ Tree
,所以如果条件允许,还是建议在查询中优先选择通过主键索引进行搜索
联合索引
在平时开发中,我们最常见的是聚集索引,但在我们需要多条件查询的时候,就不得不建立联合索引,来提高我们的查询效率
联合索引:也称复合索引,就是建立在多个字段上的索引。联合索引的数据结构依然是B+ Tree
一颗B+ Tree
只能根据一个索引值来构建,所以联合索引使用最左
的字段来构建B+ Tree
。之所以会有最左前缀匹配原则,是和联合索引的索引构建方式及存储结构 是有关系的,联合索引是使用多列索引的第一列(最左)构建的B+ Tree
联合索引的存储结构
如下图所示,表的数据如右图,ID
为主键,创建的联合索引为 (A,B
),注意联合索引顺序,左图是模拟的联合索引的B+ Tree
存储结构
叶子节点是线性排列,并且每个节点的数据排列顺序和创建索引字段的顺序一致
。如1,1,1,2,3,1
是对应着联合索引 (A,B
)字段顺序的,可以对照右图看InnoDB
会使用主键索引在B+ Tree
维护索引和数据文件,然后我们创建了一个联合索引 (A,B
)也会生成一个索引树,同样是B+ Tree
的结构,只不过它的 data 部分存储的是联合索引所在行的主键值
。如01,02,102,09
它们是联合索引所在行的主键值根据图中叶子节点的数据可以看出,所有的数据都是按照列A
进行排序的1,1,1,2,3,3,4,4;
B 列的顺序为1,2,2,1,1,5,1,5
,B
列全局
是无序的。如果使用B = 1
这种查询条件没有办法利用索引,因为联合索引首先是按A
排序的(使用最左
的字段来构建B+ Tree
),B
是无序的我们还可以发现在A
值相等的情况下,B
值又是按顺序排列的,但是这种顺序是相对局部的。如1,1,1,2,1,2,3,1,3,5
。所以最左匹配原则遇上范围查询就会停止,剩下的字段都无法使用索引。例如A = 1 and B = 2
,A,B
字段都可以使用索引,因为在A
值确定的情况下B
是相对有序的,而A > 1 and B = 2
,A
字段可以匹配上索引,但B
值不可以,因为A
的值是一个范围,在这个范围中B
是无序的
联合索引的查询流程
InnoDB
会使用主键索引在B+ Tree
维护索引和数据文件,然后我们创建了一个联合索引 (A,B
) 也会生成一个索引树,同样是B+ Tree
的结构,只不过它的 data 部分存储的是联合索引所在行的主键值
拿到联合索引所在行的主键值
后,在通过主键索引 B+ Tree
就可以直接拿到具体的行数据了覆盖索引
什么是覆盖索引
当sql
语句的所查询字段(select
列)和查询条件字段(where
子句)全都包含在一个索引(联合索引)中,那么就可以直接使用索引查询而不需要回表,这就是覆盖索引
select id,age from user where age = 10;
age
是普通索引,id
是主键索引查询的是id,age
,所以通过普通索引查询的第一步就能得到结果,不需要回表操作
不用主键索引就一定需要回表吗
先说答案:不一定!
如果查询的列本身就存在于索引(联合索引)中,也可以认为说使用了覆盖索引了,那么即使使用联合索引,一样也是不需要回表的
为什么使用覆盖索引就可以避免回表
新建一张测试表t1
如下,a
列设置为主键列,b,c,d
列建立联合索引,其他列暂时没有建立索引
执行如下sql
select b,c, d from t1 where b = 15 and c = 16 and d = 17
这里使用了覆盖索引,我们看下它的B+ Tree
数据结构
分析上图,满足条件的结果,是不是完整的显示在了叶子节点上!【我们select
查询的内容不是全表,是b,c,d
三个字段,在叶子节点上,这3
个字段是不是都已经有对应的值了】
即使我们sql
写下面这样子
select a,b,c,d from t1 where b=15 and c=16 and d=17
a
是主键列,但是在联合索引的叶子节点上存储了对应的主键值,所以依旧不需要回表操作
使用覆盖索引,我们需要select
出来的列,都已经存在了索引树的叶子节点上。所以不需要回表操作如果我们select
出来的某列,不在该联合索引的叶子节点上(比如上表的e
列),那就需要根据对应索引值,去主键索引树上回表查询对应的e
列值了
如果觉得《再谈mysql数据库之索引 联合索引 覆盖索引》对你有帮助,请点赞、收藏,并留下你的观点哦!