为什么不用数组?
数组这个数据结构,对于我们来说算是最熟悉的老朋友了,自从JAVASE时我们就接触它,对于一个有序数组,我们进行查找和修改操作效率是非常高的,并且在不考虑空洞的情况下删除操作也非常快,因为只需要将此处元素置为null,但如果我们要在数组中间的任意一个位置插入一个数据,那么必然会引起该位置后面所有数据位置的变化,也就是涉及到了数组的复制,而插入的位置越往前,所需要复制的数据就越多,该过程不仅需要消耗大量的内存,而且还会浪费大量的时间,因此从插入数据的场景来看,数组并不适合作为MySQL索引的数据结构!
为什么不用哈希表?
在MySQL中我们经常需要查找某个范围内的数据,例如between…and,>=,<=等,由于哈希表所有的key都会先经过哈希函数计算,再将数据存放到对应的位置,本来可能有序的key通过哈希函数计算之后导致其存放不有序了,因此如果我们在哈希表中进行范围查找,只能通过遍历的方式,当数据量过大的情况下,对于哈希表的整个遍历是非常耗时的。
因此从范围查询的场景来看,哈希表也不太适合作为MySQL索引的数据结构,它适合等值查询,例如我们常用的redis数据库都是key-value的形式且无序。
为什么不用二叉树?
我们最常用的二叉树有二叉搜索树,平衡树,红黑树。
无论是二叉搜索树,平衡树还是红黑树,他们的特点都是每个节点最多只有两个子节点,如果存储大量数据的话,那么树的高度就会非常高,而MySQL存储的数据最终是要到磁盘的,MySQL应用程序读取数据时,需要将数据先从磁盘加载到内存后才能继续操作,所以这中间会发生磁盘IO,而如果树的高度太高,每遍历一层结点时,就需要从磁盘读取一次数据,也就是发生一次 IO,假设数据在树高为 20 的地方,那查找一次数据就得发生 20 次 IO,耗时太长了。
因此二叉树在 MySQL 这种需要存储大量数据的场景下,是不适合当做索引的数据结构的,因为树太高,操作数据时会发生多次磁盘 IO,性能太差。
为什么使用B+Tree?
既然二叉树因为每个结点最多只有两个子结点,最终在存储大量数据时导致树太高,那么让树的每个结点尽可能多的拥有多个子结点,也就是多叉树,这样在大量储存数据时,树就低很多了,能够实现该功能的数据结构正是多叉树中典型B-Tree 和 B+Tree。
B-Tree 的特点是无论叶子结点还是非叶子结点,它都存有索引值和数据;B+Tree 的特点是只有叶子结点才会存放索引值和数据,非叶子结点只会存放索引值本身。由于一个结点的空间是有限的,B-Tree 要存放索引+数据,而 B+Tree 只需要存放索引,因此对于非叶子结点,一个结点中,B+Tree 存放的索引值数量会远远大于 B-Tree,这样就导致了每个结点中,B+Tree 能向下分出更多的叉,子结点数更多。
那么在存储同样大小数据的场景下,用 B+Tree 存储,最终树的高度会远远小于用 B-Tree 存储的高度,所以使用 B+Tree 作为 MySQL 索引的数据结构,在读取数据时,发生的磁盘 IO 次数会更少,性能更优,因此最终 MySQL 索引的数据结构使用的是 B+Tree。