查询一行数据,在物理层面就是定位到哪一个数据页中的哪一行数据。在数据页中定位数据的问题,在之前我们已经通过槽位的方式优化了查询的效率,现在我们要解决的是如何在大量的数据页中定位数据页,这就是索引的目标。(1)主键目录
InnoDB存储引擎一开始是使用主键目录的方式,将数据页号和数据页最小的主键值作为一条记录,如下图所示: 这样的话,我们要查哪一条数据就不用扫描一个数据页内的所有数据再扫描下一个了,直接通过id去主键目录看一下,通过二分查找定位到具体哪个数据页,然后数据页内部通过定位槽位,遍历那个槽位对应数据行分组找到具体的一行数据。(2)索引页
现在有一个问题就是,每张表对应的数据页都有很多,主键目录就会有大量的数据、就有可能放不下,这时InnoDB设计者们就想存放目录数据也是数据啊,为什么不可以使用数据页来放呢,就这样主键目录的信息就被移到数据页来了,而这些数据页就被称为索引页,如下图所示: 从这里我们可以知道数据页肯定不是简单只存放数据表中的数据的。好了,现在主键目录由于容量有限,我们把主键目录信息移动到了数据页中形成了索引页,但同样的问题不还是会出现吗,一个数据页的大小也才16KB,索引页本身的容量也是有限的,容量不够了该怎么办呢?为了解决索引页容量不够的问题,索引页会重新创建和升级,先把超出容量的数据放到一个新的索引页中,然后再加一层索引页,如下图所示: 由上图我们可以看到,新的一层索引页35它存放的就不是最小主键对应的数据页目录了,而是最小主键对应的索引页目录了,以此类推如果索引页35这里容量也不够呢,那就继续往上一层扩展啊,最终效果看起来就像下面一样: 大家看出来了吗,由索引页一层一层组成的结构不就是我们经常说的索引树吗,而这棵树在mysql中称之为B+索引树。 树这种数据结构天然可以使用二分法查询,所以现在如果我们要查询一条数据,从树的根节点开始通过二分法查找,以O(logn)的时间复杂度锁定数据页,然后在数据页中同样使用二分法锁定槽位,在槽位中简单遍历下不就找到数据了吗,相比于没有索引的场景,速度那可是相当快了。
3.聚簇索引、普通索引和覆盖索引
关于索引有一些常见的名词我们需要加以区分。 首先聚簇索引就是像我们上面看到的一棵树一样,它的叶子节点是一个个数据页,这些数据页中存放的都是数据表中每一行的完整数据,所以说如果B+树是以完整数据的数据页为叶子节点的,我们把这个索引树称为聚簇索引;如果一个索引的索引树,叶子节点不是以数据页为叶子节点的,就称为二级索引或普通索引。聚簇索引和普通索引最大的区别就是,聚簇索引的叶子节点存放了数据行的完整数据,而二级索引叶子节点只有数据的部分字段。 而覆盖索引本身并不是一种索引,而是一种查询数据的方式,比如我们对表table中的字段name建立了索引,然后我们执行查询如:select name from table where name like '张%',此时直接从name字段对应的B+树种查询到对应的一批name值,然后直接就返回就行了,也就是说我们想要的字段name它本来就在索引上,我们直接通过二分法高效的从树上直接摘下来就行了,而这种查询方式就称为覆盖索引。当然相比于覆盖索引方式,如果查询改为:select * from table where name like '张%',这就不是覆盖索引了,因为此时你不光要从索引树上找到具体的name,还要利用id值回表查询所有的字段。
4.索引的优缺点分析
索引的优点当然就是高效查询数据,索引将遍历链表的O(n)的查询时间复杂度优化为了O(logn)的时间复杂度。 但是索引的缺点也是很明显的,首先在时间角度上,它必须要求主键是要按顺序增长的,无序的主键会带来频繁的页分裂,影响效率;对数据库表的增删改操作的同时也需要维护索引,这部分的维护也是一块性能损耗点;在空间角度上:索引相关的数据和实际数据一样都是要占内存空间的。 所以索引虽然能够提高查询效率,但是同时也要承担它给我们的系统带来的性能损耗,从这点上来看索引并不是建的越多越好。
5.三个维度设计好索引