今天会是有offer的一天么-简单谈一谈为什么B+树适合做为索引结构

写在前面

我们知道在mysql中主要存在两种索引结构,第一种是B+树索引,第二种是Hash索引。那么先说一下什么是索引呢?为什么要使用索引呢?
索引(Index)是帮助数据库高效获取数据的一种数据结构,因为我们知道对于一个数据库而言,查询操作是最经常使用的操作,我们肯定希望查找的速度越快越好。例如说随着数据的增加,我们有10000条学生信息,假如我们要查找姓名以“张”开头的学生的详细信息,如果没有索引,我们就需要将这10000条数据中每一条数据都进行一次比较判断。当然这是一万条数据,如果是十万或者一百万甚至更多呢?这个代价肯定是非常大的,所以我们引入了索引这种结构。

在这里插入图片描述

Hash索引

在mysql中只有Memory存储引擎默认使用Hash索引。对于单条记录而言,Hash索引的查找是非常快的。因为Hash索引是使用Hash的结构存储索引,每一个键只对应一个值,可以一次定位。但要注意一点:Hash索引是不支持范围查找的。因此如果只是查找单条记录,Hash索引是一种比较好的选择;但如果我们要进行范围查找,例如查找年龄在15到18之间的学生信息,Hash索引就不是最好的选择了。

B+树索引

B+树索引是MySql中最常用的索引结构,Inodb存储引擎和MyISAM存储引擎默认都是使用B+树索引。B+树索引虽然说单条记录的查询速度比不上Hash索引,但它可以支持范围查询,要注意的是B+树索引的所有数据都是存储在叶子节点中,非叶子节点只是提供一个索引,同时每个叶子节点保存了指向下一个叶子节点的指针。
例如我们有一个Employee表,这张表有三个字段:EmployeeID,name,age。如果我们在name上加索引之后,我们在查找所有以“张”开头的员工的信息就要快很多,因为我们只要查找到第一个姓名为“”张“”的员工,继续向后进行遍历直到找到第一个姓名不为“”张“”的员工就可以了,因为以“张”开头的员工都是从左到右排列在一起的。

那为什么B+树索引可以完成这样的操作呢,这里要简单说一下B+树索引的最左匹配原则(以inobd存储引擎进行举例)。

例如说有这样一张表test(a,b,c,d);,我们建立了一个联合索引index_abc(a,b,c)。
例一:有这样一个查询语句 select * from test where a>10;当然这里是会走索引的。
例二:有这样一个查询语句 select * from test where b>5 and a>10;当然这里也是会走索引的,数据库会对这条语句进行优化等价于select * from test where a>10 and b>5。
例三:但是如果有这样一条查询语句select * from test where b>5 and c>10;要注意这里是不会走索引的,那为什么这里就不会走索引呢?
因为在对(a,b,c)建立联合索引的以后,数据库首先会按照a的大小从左向右建立搜索树,当两条数据的a相同时才会按照b的大小构建,同理最后在比较c的大小。所以如果检索数据,数据库会首先按照a的大小确定搜索方向,a如果相同才会比较b,但如果无法确定a的大小,我们是无法知道搜索方向的,因为a是第一个比较因子。或者你可以将a理解为你所在学校,b理解为所在班级(例如1班、2班等等),c理解为你的座位号;查找学生时首先确定学校,接着确定班级,最后才是座位号;那么如果查找一个学生连他在哪个学校都不知道,自然要将城市所有学校进行一个遍历。但如果只知道学校不知道班级或者座位号,我们最多也就需要遍历全校学生即可。所以对于这样一条语句select * from test where a>10 and c>5也是会走索引的,虽然第二个比较因子缺失,但只要可以确定a,我们就可以确定遍历的方向。

写到这了额外说一下,辅助索引和主键索引以及回表(以Inodb的聚簇索引举例)

例如说有这样一个表Employee(ID,name,age,sex)
其中ID是主键,我们对name和age加索引 index_name(age,name),那么会建立两棵搜索树,一棵是辅助索引一棵是主键索引;辅助索引会按照age的大小从左向右进行构建,age相同时会比较name。辅助索引的叶子节点会存储索引值(也就是age和name)和对应主键值(ID),根据主键值可以到主键索引中查找完整的数据。主键索引按照主键大小构建搜索树,叶子节点存储的是完整的数据。
所以我们要注意两点:
一.例如对于这样一条查询语句 select * from Employee where age=18;你需要首先在辅助索引中查找所有age=18数据所对应的主键值,根据主键值再到主键索引中查找完整的数据,因为我们的辅助索引中是没有保存sex的信息的,所以需要进行一次回表操作;但对于这样一条查询语句select name from Employee where age=18,我们只需要根据年龄在辅助索引中查找就可以了,因为你是对(age,name)建立索引,辅助索引中已经保存了age和name的信息,也就是可以避免回表操作。
二.对于主键的选择一定要注意,切不可选择随机数UUID作为主键,因为主键索引是按照主键的大小构建,随机数会导致频繁的节点分裂大大增加时间的消耗。举个简单的例子,UP在对mysql插入一百万条数据的时候,如果主键是自增的,可能只需要十分钟左右,但如果把主键换成UUID插入时间达到了一个小时左右。
在这里插入图片描述

那为什么要选择呢B+树作为索引而不是选择B树或者平衡二叉树呢

先说明一点,索引是存储在磁盘中,而且你是无法将所有索引一次性读入内存当中(索引是比较大的),每次只会读取一个磁盘页的数据到内存当中。那么这个磁盘的读取速度是非常非常慢的,而且要注意的一点是。磁盘每次并不是按需读取,而是会进行预读,从你所需要的位置向后继续再读取一定长度的数据放入内存中,这是由于当一个数据被使用以后,它周围的数据可能也会马上被使用。
对于平衡二叉树来讲,虽然它的查找速度为logn,是非常快的。但是相较于B树或者B+树,平衡二叉树的深度是比较深的,比如说你要查找一对父子节点,在树结构上它们看似很近(因为是父子节点,看起来好像走一步就到了),但是在物理存储上它们其实是相距非常远的(大家不要认为树结构在磁盘中也是按照这种树的形状存储的,其实树在磁盘上还是按照数组进行存储的,包括我们熟悉的大顶堆小顶堆都是按照数组进行存储的),所以说相距很远就导致了我们需要频繁的进行IO操作来读取我们所需要的数据,这个时间复杂度是非常高的。
而对于B树(注意是没有B减树的B-Tree中间不是减号,B-Tree就是B树),首先B树是矮胖的,因为它的每个叶子节点中存储的数据是非常多的,而且每个节点的大小都和磁盘页的大小一致的,在进行读取的时候只需要读取一整个节点的数据即可。因此相较于平衡二叉树,B树的IO操作是比较少的。虽然它的查找速度可能比不上平衡二叉树,但相较于磁盘IO时间,这个时间是可以忽略不计的。
最后选择B+树的原因是,B+树中的所有数据都是存放在叶子节点中的,非叶子节点只是提供一个索引。而且最重要的是B+的叶子节点是存在指向下一个叶子节点的指针的,我们可以按照指针的方向去遍历数据。这就带来了两个好处,也就是扫库和范围查询。比如你要查找年龄在15到18之间的学生信息,对于B树你需要先遍历B树找到15,接着遍历这棵树找到下一个符合条件的数据,直到找到第一个不符合的数据。而对于B+树,你只需要找到第一个年龄为15的学生,接着按照指方向向右去继续遍历叶子节点,直到找到第一个不符合条件的数据。所以选择B+树的原因就是,它在B树的基础上可以实现范围查询和方便扫库。

基本上能想到的也就这么多,有什么额外的地方Up想到之后会再进行补充。

原创文章 24 获赞 53 访问量 4万+

猜你喜欢

转载自blog.csdn.net/HZGuilty/article/details/105820795