1.什么是B+Tree?
介绍B+Tree前我们先聊一下在数据结构课程当中学习到的其他树结构,二叉搜索树,平衡二叉树搜索树(红黑树、AVL树),不懂的同学可以先去了解一下。我们知道平衡二查搜索树是对二叉搜索树的一次改进,防止退化成线性表和树高度过高的情况,使其搜索单个值的复杂度为O(logN),这也就意味着在数据必须是可排序的,同时这也是Java的TreeMap(使用的是红黑树结构)不能存null值的原因(null无法比较)。但是为什么又出现了B+树呢,红黑树的高度虽然有一定的控制,而数据库当中一般要把索引树的高度控制在3-5层,这点红黑树显然无法做到,B+树是B-树(不要读成B减树,而是B树)的加强版,是一种多路平衡搜索树,既然它是多路平衡的,那么就不在像红黑树那样只有2个子节点了,既然有多个子节点,树的高度就可以控制了,同时它也跟红黑树一样,数据是排序的,可以快速查找。B+树定义 维基百科
给大家介绍一个可以在线、可视化的学习各种树的网站。打开网站
利用该网站制作的B+Tree:
可以看到,数据都在叶子节点上,并且叶子节点用指针串联起来了,在遍历的时候更快些。
利用该网站制作的红黑树:
那么问题又来了,为什么非要用这个奇怪的B+树呢?用熟悉红黑树不行吗?
因为索引是储存在文件中,所以读取索引要通过磁盘io,而磁盘io相对内存io来说是一个相当缓慢的过程,可以理解为查找的时候并不是把整个索引树都载入内存当中(数据量一大,索引也非常庞大,甚至到几个G),而是取树的第一个节点,然后在进行比较之后再选择下一个节点。这样我们对比上面的B+树和红黑树,比如查找节点17,红黑树要磁盘io6次,而B+树只要3次,也就是说磁盘io次数大致为树的高度,这样B+树就脱颖而出了,成为实现索引的不二选择。
那么在MyISAM中是如何利用B+Tree索引组织数据的呢?什么是非聚集索引呢?
我们知道使用MyISAM引擎储存表数据会产生三个文件,参考我的这篇文章,.frm .MYD .MYI
.frm 是存放表的定义信息(MyISAM和innoDB都有这个文件) .MYD是存放具体的表的数据 .MYI就是存放该表的所有索引
那什么又是非聚集索引呢?简单来说就是索引文件和数据文件分开存储,索引树的叶子节点保存对应数据的地址,假设table表 有主键id和name(name列已经建立了索引),下面画图说明问题:
首先准备数据
对创建name列创建索引
create index idx_name on ti(name);
show index from ti;
下面这张图就表面了在MyISAM下索引是如何工作的
信息量非常大,我来一一说明:这个索引树是以建立索引的列的所有数据组织起来的,可以看出索引十分消耗存储空间,而且叶子节点存储的是对应的那行数据的地址,查找的时候先通过索引树找到对应数据的地址,再通过地址找到真正的数据。这也就是为什么不把全部的索引一次性载入内存的原因, 索引太大了。
innoDB的索引树和MyISAM是有区别的,索引和数据放在同一个文件里面,主键索引树的叶子节点直接存储数据,所以叫做聚合索引。假设上面的表用的是InnoDB,建立了主键id索引和name这一列的索引,那么数据又是如何组织起来的呢,下面画图说明:
可以看到在主键索引的叶子节点上直接存储了对应的数据,数据和索引在一起,聚合索引。而且其他的索引树的叶子节点对应的是该行数据的主键id,如果以name进行查找,先在name的索引树上找到对应的主键id,再通过得到的id进行查找。这样是为什么innoDB查询的时候比MyISAM慢的原因之一,innoDB必须涉及到主键索引,即占用内存又直接多了一次主键索引树的查找。
第一:自增的id,这个id一定是数字类型,相对于字符串而言,数字的比较速度快于字符串(字符串需要一个字母一个字母进行 比较),在B+树中,值的插入、查找、 删除都要进行比较,这样的话用数字作为主键比用字符串做为主键在比较速度上更有优势。
第二:自增的id对比乱序的UUID有什么优势呢,可以看到乱序的UUID在B+树中的插入位置是非常随机的,而自增id每次都往B+树的最右边进行插入,这样B+树的分裂操作更简单,而且底层分配空间是分配一段连续的空间,这样每次都像往数组的最后插入数据,可以更合理的利用连续分配的空间,以免造成碎片过多。
第三:可以看到索引也是非常占用磁盘空间的,对字段小的列建索引更合理,比如对int型的列建索引就比对varchar(50)的列建索引更加节省磁盘空间。
总结:索引小,查找快,分裂有序
参考教程:
咕泡学院VIP教学