MySQL 为啥采用B+Tree结构实现索引

从数据结构的角度来看MyISAM支持B+树索引、hash索引、fulltext索引(全文索引)、r-tree 索引(用于GIS数据类型创建SPATIAL索引),其中hash索引只能等值的查询,不能进行范围查询,另外只有Memory存储引擎显示支持hash索引。
不同的存储引擎支持不同的索引类型。下面来简单简单的介绍下主要的一些存储引擎支持的索引类型。

1. 不同的存储引擎支持的索引类型

1 ) Innodb 存储引擎
下面来打开一张Innodb存储引擎的表.


Innodb存储引擎支持的索引.png

我们看到Innodb存储引擎支持5种索引类型:primary(主键),index(普通索引),unique(唯一索引),SPATIAL索引(空间索引),fulltext(全文索引)。其中前三种属于B+树结构的索引。另外Innodb支持自适应hash索引(使用散列表的数据结构),它是Innodb 内部创建的,DBA无法干预的。
2) MyISAM 存储引擎
下面打开一张MyISAM的数据表:


MyISAM.png

我们发现和Innodb存储引擎支持的索引类型是一样的,这里就不展开说了。不过需要注意的是虽然支持的索引类型是一样的,但是底层的实现略有不同。比如B+树类型结构的索引,MyISAM 是非聚集索引而Innodb 支持聚集索引(主键索引)。这里不展开说了,小伙伴们可以查阅相关的资料。
3) Memory存储引擎
这种存储引擎我们一般使用得相对来说比较少,Memory存储引擎将数据存放在内存中,如果数据库重启或者发生崩溃,表中的数据将会消失。它非常适合用于存储临时数据的临时表(需要注意的是临时表和内存表有一定的区别的,memory创建的表示属于内存表,具体的区别这里就不展开说了),以及数据仓库中的纬度表。关于Memory存储引擎就不详细介绍了。

Memory存储引擎默认使用Hash索引,不过它也支持B+树索引,如果读者希望使用B树型,则在创建的时候可以引用。比如:

mysql > create table t1 (
                 id int, index using btree(id)
          ) engine = memory;

从上面我们了解到MySQL底层的索引实现不只是有B+tree,还有hashtable(散列表)等。不过B+tree索引是大部分情况下使用的一种索引。下面来介绍下MySQL为啥会使用B+tree作为主要的索引实现。
索引的作用是提高查询效率,其实实现方式有很多种,常见的索引模型有哈希表、有序列表、搜索树(查找树)等。下面来具体分析下这几种数据结构的优缺点。

2. 不同的数据结构实现索引

1) 哈希表
一种以key-value键值对的方式存储数据结构,通过指定的key可以找到对应的value。
哈希把值放在数组里,用一个哈希函数把key换算成一个确定的位置,然后把value放在数组的这个位置。但是,多个key值经过换算,可能会出现同一个值,即哈希冲突,常见的解决办法是链地址法,即将所有的相同的Hash值的key放在一个链表中,这样,无论有多少个冲突,都只是在当前位置给单链表增加节点。
由于hash表的数据结构使得等值的搜索很快,但是如果要范围查找的话,可能就需要把所有的数据取出来后进行对比看符不符合条件,这样将会很慢,面对复杂的业务场景显然是不合适的。
2)有序列表
有序列表适合等值和范围查找,但是如果要频繁的更新的话效率是很低的,需要频繁的移动数据,所以也不太适合业务场景。
3) 查找树
a. 二叉树
每个节点的左儿子小于父节点,父节点小于有儿子。
二叉树会出现一个比较极端的情况就是当数据分布不均匀的时候,这个二叉树可能只有左腿或者只有右腿,这个时候就退化成了一个有序链表。查找就变成了顺序查找,时间复杂度为O(n),当数据量比较大的时候效率是很低的。
b. 平衡二叉树
平衡二叉树(AVL树)在符合二叉查找树的条件下,还满足任何节点的两个子树的高度最大差为1。当删除节点或者插入节点都可能导致平衡树失去平衡,这个时候就会通过旋转来达到平衡。这里不展开说了。虽然平衡二叉树可以避免出现极端情况,但是它每个节点存储的数据太少,往往会导致树比较高,在真实的查询中会导致频繁的磁盘访问。另外的话,为了保持平衡二叉树的特性在更新后需要通过旋转来达到平衡,这样会损耗一些性能。
c. B-tree(平衡多路查找树)
B-Tree是为磁盘等外存储设备设计的一种平衡查找树,下面来简单了解下磁盘的相关知识。(以传统的磁盘来看)
系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一磁盘块(扇区)中的数据会被一次性读取出来,假设要查询的表和其他表都在同一个磁盘块内,加载磁盘块的时候除了读取要查询的表,其他表也一并被读取出来。
MySQL 存储引擎是以页为单位进行磁盘存储,这个页的大小往往是大于磁盘块的大小的,也就是说每次MySQL从磁盘中加载数据到内存时是一次加载好几个相邻磁盘块的数据。下面笔者从网上找到一张b-tree图,来帮助大家理解。

b-tree树结构.png

b-tree 特征:
(1) 关键字集合分布在整颗树中;
(2) 任何一个关键字出现且只出现一次在节点中;
(3) 搜索有可能在非叶子节点结束;
(4) 在关键字全集内做一次查找,性能逼近二分查找算法。
b-tree 的一个最大特征就是每个节点都会存放数据,所以存放索引的空间相对较少,数据查询时磁盘I/O的次数仍然比较多。
d. B+Tree
B+Tree是B-tree的变体,也是一种多路平衡查找树,下面笔者从网上找了一张图来帮助大家理解:
B+Tree.png

B+tree 的特征
(1) 所有关键字存储在叶子节点,非叶子节点不存储真正的数据,数据存储在叶子节点。
(2) 为所有叶子节点增加了一个链指针(双向的)。
正是因为B+tree 非叶子节点存储的都是索引,所以B+树单次磁盘I/O的信息量相比B-Tree要大,I/O效率更高。其次叶子节点增加的链指针也为范围查找带来了遍历,比如只要找到某个值后,如果要查询大于它的一个区间的值,只需要沿着指针查找即可,不需要再次进行搜索,大大提高了效率。不过需要注意的是:不同的存储引擎的B+tree略有不同,比如MyISAM存储引擎的B+Tree叶子节点数据存放的是指向数据行的地址,而不是行数据。
当然可能还有其他的一些查找树,这里就不再一一分析了,小伙伴可以沿着这个思路去分析MySQL为啥不使用它们而是使用B+Tree。

3. 总结

MySQL 使用B+Tree作为索引实现的主要原因是考虑到磁盘I/O的效率。不过现在出现了一种固态硬盘,相比传统的磁盘而言,不管是读取,写入还是存储容量上都大大的提升了,不过目前的话价格比较昂贵而且还没有普遍流行起来,也许等固态硬盘流行起来后,现在说的这个磁盘I/O效率的瓶颈可能会没那么突出,到时可能MySQL 也会考虑使用别的一种数据结构来代替B+Tree也不一定。

以上是读者对MySQL 使用B+Tree 作为索引实现的一些理解吧,小伙伴如果有需要补充的,欢迎在下面给我留言哦,看到会及时回复的。

你可能感兴趣的:(MySQL 为啥采用B+Tree结构实现索引)