学习本章之前,先要了解红黑树 【算法】红黑树原理和算法介绍,以及B树、B+树 【算法】B树、B+树详解
最近重新学习MySQL,发现自己一直知道MySQL索引用到了B+树,引发思考,为什么一定要是B+树,其他树或者其他数据结构不可以吗?
为什么不是用红黑树
1、将大量数据全部放入内存组织成RBT结构显然是不实际的。实际上,像OS中的文件目录存储,数据库中的文件索引结构的存储…. 都不可能在内存中建立查找结构。数据必须在磁盘中建立好这个结构。
2、这就涉及到磁盘的存储原理了,操作系统读写磁盘的基本单位是扇区,而文件系统的基本单位是簇(Cluster)(每个簇或者块可以包括2、4、8、16、32、64…2的n次方个扇区。)。意思就是,磁盘读写有一个最少内容的限制,即使我们只需要这个簇上的一个字节,我们也必须把整个簇的内容都读完,那么现在就有一个悲催的事情了,如果一个父节点只有2个子结点,并不能填满一个簇上的所有内容,那多余的地方就浪费了,考虑到磁盘的存储原理,B/B+树应运而生了。
3、由于B/B+树分支比二叉树多,所以相同数量的内容,B+树的深度更浅。B+树的深度就代表了磁盘的 I/O 次数。
4、数据库设计的时候B+树有多少个分支都是按照磁盘上一个簇最多能放多少节点设计的,因此一般来说,涉及到磁盘上查询的数据结构,都是使用B/B+树
为什么说B+树比B树更适合数据库索引?
1)B+树的磁盘读写代价更低
B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了;
2)B+树查询效率更加稳定
由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当;
3)B+树便于范围查询(最重要的原因,范围查找是数据库的常态)
B树在提高了IO性能的同时并没有解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低;
磁盘IO与预读
计算机存储设备一般分为两种:内存储器(main memory)和外存储器(external memory)。
内存储器为内存,内存存取速度快,但容量小,价格昂贵,而且不能长期保存数据(在不通电情况下数据会消失)。
外存储器即为磁盘读取,磁盘读取数据靠的是机械运动,每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分,寻道时间指的是磁臂移动到指定磁道所需要的时间,主流磁盘一般在5ms以下;旋转延迟就是我们经常听说的磁盘转速,比如一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms;传输时间指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。那么访问一次磁盘的时间,即一次磁盘IO的时间约等于5+4.17 = 9ms左右,听起来还挺不错的,但要知道一台500 -MIPS的机器每秒可以执行5亿条指令,因为指令依靠的是电的性质,换句话说执行一次IO的时间可以执行40万条指令,数据库动辄十万百万乃至千万级数据,每次9毫秒的时间,显然是个灾难。下图是计算机硬件延迟的对比图,供大家参考:
考虑到磁盘IO是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助。
事实1 : 不同容量的存储器,访问速度差异悬殊。
- 磁盘(ms级别) << 内存(ns级别), 100000倍
- 若内存访问需要1s,则一次外存访问需要一天
- 为了避免1次外存访问,宁愿访问内存100次...所以将
最常用
的数据存储在最快的存储器中
事实2 : 从磁盘中读 1 B,与读写 1KB 的时间成本几乎一样
从以上数据中可以总结出一个道理,索引查询的数据主要受限于硬盘的I/O速度,查询I/O次数越少,速度越快,所以B树的结构才应需求而生;B树的每个节点的元素可以视为一次I/O读取,树的高度表示最多的I/O次数,在相同数量的总元素个数下,每个节点的元素个数越多,高度越低,查询所需的I/O次数越少;假设,一次硬盘一次I/O数据为8K,索引用int(4字节)类型数据建立,理论上一个节点最多可以为2000个元素,2000*2000*2000=8000000000,80亿条的数据只需3次I/O(理论值),可想而知,B树做为索引的查询效率有多高;
另外也可以看出同样的总元素个数,查询效率和树的高度密切相关