1.索引结构
1.1.B-Tree(B树)和B+Tree(B+树)
前面是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为例(二级索引存储数据量的计算原理类似,但是叶子节点和非叶子节点上存储的数据格式略有差异),我们可以简单算一下:
①.计算机在存储数据的时候,最小的存储单元是扇区,一个扇区的大小是512字节,而文件系统(例如XFS,EXT4)最小单元是块,一个块的大小是4KB.INNODB引擎存储数据的时候,是以页为单位的,每个数据页的大小默认是16KB,即四个块;
②.假设数据库中一条记录是1KB,那么一个页就可以存放16条数据(叶子结点);对于非叶子节点存储的则是主键值+指针,在InnoDB中,一个指针的大小是6个字节,假设我们的主键是bigint,那么主键占8个字节(当然还有其他一些头信息也会占用字节我们这里就不考虑了),大概算一下:
16*1024/(8+6) = 1170
即
一个非叶子节点可以指向1170个页
,那么一个三层的B+Tree可以存储的数据量为:
1170*1170*16 = 21902400 //可以存放2100万条数据
③.在InnoDB存储引擎中,B+Tree的高度一般为2-4层,这就可以满足千万级数据的存储,查找数据的时候,一次页的查找代表一次IO,那我们通过主键索引查询的时候,其实
最多只需要2-4次IO操作
就可以了;
2.两类索引
1.MySQL中的索引有很多种不同的分类方式,可以按照数据结构分,可以按照逻辑角度分,也可以按照物理存储分,其中按照物理存储方式,可以分为聚簇索引和非聚簇索引;
2.我们日常所说的主键索引,其实就是聚簇索引(Clustered Index)
;主键索引之外,其他的都是称之为非主键索引,非主键索引也被称为二级索引(Secondary Index),或者叫辅助索引;
3.对于主键索引和非主键索引,使用的数据结构都是B+Tree
,唯一的区别在于叶子节点中存储的内容
不同(最大的区别):
①.
主键索引的叶子节点存储的是一行完整的数据;
②.
非主键索引的叶子节点存储的则是主键值;
4.当我们进行查询的时候:
①.如果是
通过主键索引来查询数据
,例如"select * from user where id = 100",那么此时只需要搜索主键索引的B+Tree就可以找到数据;
②.如果是
通过非主键索引来查询数据
,例如"select * from user where username=‘zhangsan’",那么此时需要先搜索username这一列索引的B+Tree,搜索完成之后得到主键值,然后再去搜索主键索引的B+Tree,才可以取到一行完整的数据;
对于第二种查询方式而言,一共搜索了两棵B+Tree,第一次搜索非主键索引B+Tree,获取主键值之后再去搜索主键索引的B+Tree,这个过程就是所谓的回表;
3.一定会回表吗?
1.如果不使用主键索引就一定需要回表吗?
不一定!
2.如果查询的列本身就存在于索引中,那么即使使用二级索引,一样也是不需要回表的
;
3.1.例子:
①.表结构如下:
如上图,
uname和address字段组成一个复合索引
,那么此时虽然这是一个二级索引,但是索引树的叶子节点中除了保存主键值,也保存了address和uname的值;
使用非主键字段查询数据,查看执行计划:
可以看到,此时使用到了uname作为查询条件,但是最后的extra的值为
Using index
,这就表示用到了索引覆盖扫描(覆盖索引),此时直接从索引中过滤不需要的记录并返回命中的结果,这一步是在MySQL服务层完成的,并不需要回表;
4.扩展: 为什么要在数据库中建议使用自增主键?
1>.自增主键往往占用空间比较小,int占4个字节,bigint占8个字节.由于二级索引的叶子节点存储的就是主键值,所以如果主键占用空间小,意味着二级索引的叶子节点将来占用的空间小(间接降低B+Tree的高度,一提高搜索效率);
2>.自增主键插入的时候比较快,直接插入即可,不会涉及到叶子节点分裂等问题(不需要挪动其他记录).
而其他非自增主键插入的时候,可能要插入到两个已有的数据中间,就有可能导致叶子节点分裂等问题,插入效率低(要挪动其他记录);