B树、B-树、B+树的定义和区分

B树

另一个名字就是二叉树索引。
1.每个节点的儿子节点都是两个,左边是比该节点小的,右边是比该节点大的,这两个索引的儿子节点同样是这种情况
2.所有的节点都是一个关键字

正常情况来说如下:
在这里插入图片描述
搜索的时候是从根节点开始搜索的。
比如搜索的是35,则直接命中。如果搜索“28“,那就是比35小,左边节点,“17”,“28”比“17”大,则右边节点,直到命中。
但是这种结构在进行节点删除等操作之后有可能出现以下情况:在这里插入图片描述
这时候虽然也是索引,但是搜索已经是线性的了,并没有省多少劲儿。所以我们要尽量保证所有节点两边的平衡才是关键,也就是平衡算法,平衡算法是一种在B树中插入和删除节点的策略。

这时候就需要一个更加严谨,并且便于维护的索引结构。

B- 树

我们把所有的数据进行折半块查找,比如一共100条数据。在30和60的地方分一下,存放30和60的节点就是根节点。30和60就是该节点的关键字,100也就是分成了3份,这个节点自动创建三个指针。这三份就是根节点的子节点,(敲黑板划重点,子节点的个数=关键字个数+1,没有子节点就不说了)。下面继续分,拿其中一个子节点举例,把30分成3分,10和20当成关键字,下面继续是三个子节点。。。依次类推。
如下
在这里插入图片描述
在这里插入图片描述
而这种类型就是B-树,它的所有关键字都分布在节点中。不一定就是我写的两个。
特点如下:

  1. 定义任意非叶子结点最多只有M个儿子;且M>2;
  2. 根结点的儿子数为[2, M];
  3. 除根结点以外的非叶子结点的儿子数为[M/2, M];
  4. 每个结点存放至少M/2-1(取上整)和至多M-1个关键字(至少2个关键字);
  5. 非叶子结点的关键字个数=指向儿子的指针个数-1;
  6. 非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
  7. 非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;
  8. 所有叶子结点位于同一层;

ps:k是关键字,p是指针(就是图中的箭头),只要有子节点的 都是非叶子节点。

B-树的搜索依然是从根节点开始,对关键字序列进行二分查找,如果找到则命中。如果没有,就按照相应的范围根据指针去相应的子节点。直到命中。

所以B-树的性能总是等价于二分查找(与M值无关),也就没有B树平衡的问题;

由于M/2的限制,在插入结点时,如果结点已满,需要将结点分裂为两个各占M/2的结点;删除结点时,需将两个不足M/2的兄弟结点合并;

有的同学就说了,那数据量大的时候不是依然会查询到最底层的叶子节点。 这就是B-树的缺点,但是相比B树来说已经高级了许多。

B+树

其实B+树与B-树相差并不大,由于B-树可能会最高级的根节点查询到最低级的叶子节点。那我们可以从叶子节点开始查啊,这就产生了B+树结构。

在每一个叶子节点做一个标记,把这些标记存起来,每次查的时候可以在查询根节点的时候从叶子节点也开始查,这样是不是就省了好多时间呢。
而这些标记就是链指针。把这些链指针存进一张表中,这个表就是稠密索引.
在这里插入图片描述
这些链指针在链表中是有序存储的,在搜索中能省大量的时间。
那这些链指针可不可以加在所有的节点中呢,答案是可以的,除了根节点,所有的节点都可以加上链指针。这就是B*树索引

B*树

在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3;
在这里插入图片描述

后记:写这篇文章,其实是因为今天在一个大神开的java群中,有人问什么是回表查询,为什么会有回表查询。讨论的时候发现自己对索引一知半解,刚好下午有时间,就查资料写了这篇文章。

关于回表查询
比如select a from T where b=?
如果a没有索引,那在查询的时候先得得到的是b对应这条数据所在的行数。拿着这个行数,再去表中查询这条数据,得到a字段。而拿着这个行数去得到a字段的动作,就是回表查询

我们如何避免回表查询呢,首先就是不要用 ” * “ 查询,因为这时候会默认查询的字段没有索引,必定进行回表查询。

在一张表中单独查询次数多的字段,尽量加上索引吧。但是也不能遇见一个查询字段就加索引,那样的效率会越来越低,得不偿失。毕竟任何事情都是有个度的

引用:http://www.javashuo.com/article/p-sptdtuym-k.html
https://blog.csdn.net/idber/article/details/8087521