mysql数据库树_MySQL数据库索引之B+树

一、B+树是什么

B+ 树是一种树型数据结构,通常用于数据库和操作系统的文件系统中。B+ 树的特点是能够保持数据稳定有序,其插入与修改操作拥有较稳定的对数时间复杂度。B+ 树元素自底向上插入,这与二叉树恰好相反。

B+ 树的创造者Rudolf Bayer没有解释B代表什么。最常见的观点是B代表平衡(balanced),因为所有的叶子节点在树中都在相同的级别上。B也可能代表Bayer,或者是波音(Boeing),因为他曾经工作于波音科学研究实验室

1)B+树的节点

在 B+ 树中的节点通常被表示为一组有序的元素和子指针。如果此B+树的序数(order)是m ,则除了根之外的每个节点都包含最少 [m/2] 个元素最多 [m-1] 个元素,对于任意的节点有最多 m 个子指针。对于所有内部节点,子指针的数目总是比元素的数目多一个.

2)B+树相应的操作

1.查找

2.插入

3.删除

3)B+树的特点

1.每个节点中子节点的个数不能超过 m,也不能小于 m/2;

2.根节点的子节点个数可以不超过 m/2,这是一个例外;

3.m 叉树只存储索引,并不真正存储数据,这个有点儿类似跳表;

4.通过链表将叶子节点串联在一起,这样可以方便按区间查找;

5.一般情况,根节点会被存储在内存中,其他节点存储在磁盘中

4)在这里说下B 树、B- 树

B- 树就是 B 树,英文翻译都是 B-Tree,这里的“-”并不是相对 B+ 树中的“+”,而只是一个连接符。

而 B 树实际上是低级版的 B+ 树,或者说 B+ 树是 B 树的改进版。B 树跟 B+ 树的不同点主要集中在这几个地方:

B+ 树中的节点不存储数据,只是索引,而 B 树中的节点存储数据;

B 树中的叶子节点并不需要链表来串联。

也就是说,B 树只是一个每个节点的子节点个数不能小于 m/2 的 m 叉树

B-Tree:

DxNjqqbuFYUbAAAAAElFTkSuQmCC

B+Tree:

A+JEflgAjvvpAAAAAElFTkSuQmCC

二、MySQL数据库索引为啥使用B+树作为底层数据结构

1)明确数据库索引是解决什么问题的

这里举最常用的两个功能性需求:

1.根据某个值查找数据,比如 select * from user where id=1234;

2.根据区间值来查找某些数据,比如 select * from user where id > 1234 and id < 2345;

然后是非功能性需求比如性能方面的:

主要考察时间和空间两方面,也就是执行效率和存储空间。

在执行效率方面,我们希望通过索引,查询数据的效率尽可能的高;在存储空间方面,我们希望索引不要消耗太多的内存空间。

2)明确了待解决的问题,然后就是用已知的数据结构解决问题

我们已知的支持快速查询、插入等操作的动态数据结构有:散列表、平衡二叉查找树、跳表、B+树

1.散列表 (不满足)

散列表的查询性能很好,时间复杂度是 O(1)。但是,散列表不能支持按照区间快速查找数据

2.平衡二叉查找树(不满足)

平衡二叉查找树查询的性能也很高,时间复杂度是 O(logn)。而且,对树进行中序遍历,我们还可以得到一个从小到大有序的数据序列,但这仍然不足以支持按照区间快速查找数据

3.跳表(满足)

跳表是在链表之上加上多层索引构成的。它支持快速地插入、查找、删除数据,对应的时间复杂度是 O(logn)。并且,跳表也支持按照区间快速地查找数据。我们只需要定位到区间起点值对应在链表中的结点,然后从这个结点开始,顺序遍历链表,直到区间终点对应的结点为止,这期间遍历得到的数据就是满足区间值的数据。

4.B+树 (满足)

B+树这种数据结构跟跳表非常相似,所以适合做数据库索引。不过,它是通过二叉查找树演化过来的,而非跳表。

3)确定使用B+树后,那么如何解决索引占用太多内存的问题呢?

因为作为数据库索引,存储的数据可能非常的大,比如给一亿个数据构建索引,那索引中会包含大约 1 亿个节点,每个节点假设占用 16 个字节,那就需要大约 1GB 的内存空间。给一张表建立索引,我们需要 1GB 的内存空间。如果我们要给 10 张表建立索引,那对内存的需求是无法满足的。

那么我们就需要借助空间换时间的思路,把索引存储在硬盘中,而非内存中。我们都知道,硬盘是一个非常慢速的存储设备。通常内存的访问速度是纳秒级别的,而磁盘访问的速度是毫秒级别的。读取同样大小的数据,从磁盘中读取花费的时间,是从内存中读取所花费时间的上万倍,甚至几十万倍。

将索引存储在硬盘中后,尽管减少了内存消耗,但是在数据查找的过程中,需要读取磁盘中的索引,因此数据查询效率就相应降低很多。对于B+树来说,每个节点的读取(或者访问),都对应一次磁盘 IO 操作。树的高度就等于每次查询数据时磁盘 IO 操作的次数。我们优化的重点就是尽量减少磁盘 IO 操作,也就是,尽量降低树的高度。

4)为了提高B+树索引的效率,那么如何降低树的高度?

对于B+树来说,其实可以看做是 m 叉树,那么树的高度就取决于 m 的大小。比如给 16 个数据构建二叉树索引,树的高度是 4,查找一个数据,就需要 4 个磁盘 IO 操作(如果根节点存储在内存中,其他结点存储在磁盘中),如果对 16 个数据构建五叉树索引,那高度只有 2,查找一个数据,对应只需要 2 次磁盘操作。如果 m 叉树中的 m 是 100,那对一亿个数据构建索引,树的高度也只是 3,最多只要 3 次磁盘 IO 就能获取到数据。磁盘 IO 变少了,查找数据的效率也就提高了。

m越大,树的高度就越小,那么m是不是越大越好呢?不是,不管是内存中的数据,还是磁盘中的数据,操作系统都是按页(一页大小通常是 4KB,这个值可以通过 getconfig PAGE_SIZE 命令查看)来读取的,一次会读一页的数据。如果要读取的数据量超过一页的大小,就会触发多次 IO 操作。所以,我们在选择 m 大小的时候,要尽量让每个节点的大小等于一个页的大小。读取一个节点,只需要一次磁盘 IO 操作。

5)索引的使用也会导致写入数据效率的下降

尽管索引可以提高数据库的查询效率,但是,作为一名开发工程师,你应该也知道,索引有利也有弊,它也会让写入数据的效率下降

/*** 这是 B+ 树非叶子节点的定义。

*

* 假设 keywords=[3, 5, 8, 10]

*

* 4 个键值将数据分为 5 个区间:(-INF,3), [3,5), [5,8), [8,10), [10,INF)

*

* 5 个区间分别对应:children[0]...children[4]

*

* m 值是事先计算得到的,计算的依据是让所有信息的大小正好等于页的大小:

*

* PAGE_SIZE = (m-1)*4[keywordss 大小]+m*8[children 大小]*/

public classBPlusTreeNode {public static int m = 5; //5 叉树

public int[] keywords = new int[m - 1]; //键值,用来划分数据区间

public BPlusTreeNode[] children = new BPlusTreeNode[m];//保存子节点指针

}/*** 这是 B+ 树中叶子节点的定义。

*

* B+ 树中的叶子节点跟内部结点是不一样的,

*

* 叶子节点存储的是值,而非区间。

*

* 这个定义里,每个叶子节点存储 3 个数据行的键值及地址信息。

*

* k 值是事先计算得到的,计算的依据是让所有信息的大小正好等于页的大小:

*

* PAGE_SIZE = k*4[keyw.. 大小]+k*8[dataAd.. 大小]+8[prev 大小]+8[next 大小]*/

public classBPlusTreeLeafNode {public static int k = 3;public int[] keywords = new int[k]; //数据的键值

public long[] dataAddress = new long[k]; //数据地址

public BPlusTreeLeafNode prev; //这个结点在链表中的前驱结点

public BPlusTreeLeafNode next; //这个结点在链表中的后继结点

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值