Mysql索引之B-Tree和B+Tree
B-Tree
首先我们来看,B-tree这种数据结构是怎么样的,请看下图:
B-tree实际上可以说是多叉平衡树,它的局部明显也符合二叉树的特性,B-Tree通过在每个节点进行一个"横向的扩展",让这种数据结构及时在数据量很庞大的时候,也能控制Tree的高度!观察一下它的每一个节点,看出它实际上是由两部分组成,一个是索引值,一个是data;
那么这个data我们可以简单的理解,它是存储了这个索引值所在的那行数据的对应的磁盘文件地址指针!或者是除了索引所在那行的除了索引字段的其他字段的数据集合!(这里就涉及到一个聚簇索引和非聚簇索引的概念)等会来说
其次我们来看一个完整的通过B-Tree这种数据结构来查询数据的一个过程
首先呢,先从根节点开始查,一般来说,会事先把根节点load到内存中,然后呢在内存中进行比对,比如说找的是23那行数据,开始比对,发现23>15,然后往右进行比对,发现23<60,那么就定位到15的右边的第一个子节点(因为二叉树的一个特性),此时会把这个子节点load到内存中,然后在内存中进行比对,开始比对,发现23>20那么此时就定位到右边的子节点,同样的操作,直至定位到索引值为23的那个节点,并根据那个节点上的data里面保存的磁盘文件地址指针拿到需要的那行数据…
B+Tree
首先我们来看一下B+Tree这种数据结构是啥样的?
基于myisam引擎的B+Tree
基于innodb引擎的B+Tree
仔细观察一下,很容易发现,这两种存储引擎下面的B+Tree这种数据结构在叶子节点有很明确的区别;
- MyIsAm中B+Tree的叶子节点只存储了索引数据和索引所在行的磁盘文件地址指针
- Innodb中B+Tree的叶子节点不仅存储了索引数据,并且还存储了索引所在行的所有其他字段的数据
那么这里会引出一个概念----聚簇索引
其实聚簇索引可以用一句话来描述:叶子节点保存了完整的数据或作索引数据和字段数据放在了一块!
再来说说这两种存储引擎的数据存储区别:
如上图,userinfo是基于innodb存储引擎的表, 有两种类型的文件,一种是frm,一种是ibd文件
- frm文件是存储表结构定义的数据
- ibd文件实际上表数据,并且是基于B+Tree这种数据结构组织的索引结构文件,也就是表数据和索引数据放在了一块,并且用了B+Tree这种数据结构来组织在一起
显然client就是基于myisam存储引擎的表,有三种类型的文件,一种是frm,一种是MYD,还有一种是MYI
- frm文件是存储表结构定义的数据
- MYD文件是存储表数据
- MYI文件是存储索引数据
显然myisam存储引擎的索引是非聚簇索引,innodb的则是聚簇索引
它的查询数据的过程与B-Tree一致,可以参照上文
对比B-Tree和B+Tree这两种数据结构!不难发现他们之间的区别
可以说B+Tree是B-Tree的一个变种,B+Tree是在B-Tree的基础上做的一个优化的产物;
通过上图我们可以看到有一下几个明显的区别
- B-Tree中的每个索引节点存放了索引值+data(索引所在行的磁盘文件地址指针),而B+Tree则弃用了这种存储方式,在非叶子节点中,每个索引节点只有索引值,这样一来每个非叶子节点可以存储更多的索引节点,提升了横向扩展的效果!并且B+Tree中,它的索引数据在每个叶子节点上面做了冗余,也就是说在每个非叶子节点上面的索引数据在叶子节点有一份完整的备份;
- B+Tree的叶子节点中的索引节点用指针连接,并且维护的数据特点是从左往右是增长的,当然这就是二叉树的特性,a<b<c,那么自然有a<c;那么为什么要维护这种数据呢,就是为了让B+Tree更好的支持范围查询;
试想一下,如果没有指针连接,那么当你进行范围查询的时候,假设是这么一条sql语句,id是索引,并且是bigint类型,自增的状态;select user_name,pass_word from user_info where id > 15
那么会是什么情况?
会是这样的情况,先从根节点开始比对,发现大于15应该走右边子节点,然后到了第二层的节点,同理也是刚好走15的右边,但是到了叶子节点,发现大于15的要走右边,找到了18.然后呢?然后是不是就又得从当前节点的上一层也就是父节点开始比对?但是如果我们加了指针连接呢?指针一口气给你往右遍历,拿到所有的id>15的你需要的数据,这不就极大的缩短了时间吗?Hash这种数据结构致命的缺陷也是在这里,所以我们不得不佩服,B+Tree这种这么优秀的数据结构的设计者!!实在是太厉害了!当然人家肯定也是经过很长时间的沉淀,很长的时间的不断学习才会有这种产物!!在这里,想给点鸡汤,作为一个程序员,不持续不断的学习和总结,是对自己极大的不负责任,我们一定要给自己在职业生涯的起点就规划好自己的学习路线!选择能帮助你成长的领导也是非常重要!!!
B+Tree实在是非常优秀,它的出现让我们在千万数据中查询某一行数据都不再是噩梦!
那么我们就来具体说说,它究竟是怎么做到的!
- 先来看看每个节点的默认大小
我们可以看到每个节点的默认大小是16384字节,也就是16kb;
一般情况下,我们的索引值选为bigint型;最大的一个好处是便于比较,并且在自增的情况下,B+Tree对索引的范围查询支持非常不错!!!后续会来解释为什么这样选择! - bigint类型的数据占8个字节,指针的大小一般也为4或8个字节,那我们先姑且算是8个字节,那么也就是一个非叶子节点大小一共是16个字节,那么我们可以算一下,一个节点能存放多少个这样的叶子节点:16384/16=1024(个),那么我们再来看,当树的高度为3的时候,叶子节点大概能存下多少索引:那么假设一个叶子节点中的索引值+data所占大小为1kb,那么一个叶子节点可以放16个这样的索引,那么我们来计算一下,也就是1024102416=1600多万个索引,也就是说当这颗B+Tree被撑满的情况下,叶子节点可以存放1600多万的索引个数,我们可能恍然大悟,其实这就是B+Tree能支持千万级数据量的快速查询的原因所在!