首先,讲一下什么是索引
索引就好比是书的目录,比如当我们查看一本字典的时候,目录就相当于我们的对照表,
也就是说,目录里的内容来源于书本但却独立于书本,但是呢,目录又是这本书里的一页(●ˇ∀ˇ●),就酱
然后,讲一下访存的问题
我们都知道,计算机的存储管理是分层的,各层次之间的速度差距比较大,尤其是辅存(磁盘)和主存之间的速度
那么,我们都知道磁盘访问比较慢,为啥会这么慢呢?
首先,cpu访问磁盘时,磁盘主要干了这些事
1. 寻道:磁头摆一摆,找到对应的柱面
2. 定位:盘面转一转,磁头定位到指定扇区
在这两个步骤中,因为是机械操作,我们都晓得,这机械运动哪能比得上人家主存的电信号传播呢( ̄_, ̄ ),所以自然就慢了不少,磁盘访问慢了,访问效率自然就跟不上了
既然这么慢,那我们肯定就得想办法,能不能在不改变这种机械运动的情况下提升一下这个访存效率呢?
这就好比你查字典,给你一个字,你翻一翻,给你一堆字,你翻n翻,查半年?
能不能有个好一点的目录,一搜搜一篇,命中率嘎嘎的那种?
所以,这就要求我们建立一个好的索引也就是所谓的好目录来帮助我们啦
磁盘读写原理
这里主要介绍一下磁盘预读原理,在这之前,要讲一下预读原理的基础,程序的局部性原理
程序的局部性原理是指程序在执行时呈现出局部性规律,即在一段时间内,整个程序的执行仅限于程序中的某一部分。相应地,执行所访问的存储空间也局限于某个内存区域(也就是说,这个程序在这一小段时间内只会用这一部分内存的这一小撮数据)
磁盘预读: 有了这个局部性原理,那我就想了,这程序执行依次要一个数据,现在我知道它要的数据基本都是一堆一堆的凑在一起的,那我就可以在它要之前我这个磁盘先读着我这个扇区下面的内容,反正我这磁盘转着挺快的,顺序读取效率蛮高,我先读着,你要不要另说(●ˇ∀ˇ●)
通过预读呢,我们就可以提前准备好要用数据,提升I/O效率
那么我们怎么去预读呢,预读多大呢?
预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。(抄抄抄(❤ ω ❤))
B树和B+树
具体结构不说了,大家既然看这篇文章想必是都懂
这两种数据结构其实就是我们要构造的索引所用到的数据类型
在实际设计中,我们把一个结点设为一个页,为什么这么干呢,因为磁盘预读是以页为单位的,所以这样的话一页就代表访问一次磁盘,也就是代表一次I/O操作。
下面我们分别来看一下这两种不同的索引:
B树: 假设有一棵M阶的B树,那么每个结点最多M-1个关键字。我们通过一次磁盘访存就可以访问这个结点,也就是M-1个关键字。一般树的高度不会超过3层,也就是说,查找一个关键码只需要3次磁盘操作就可以了。
注意B树的非叶结点不单单只有key值,还有key对应数据在磁盘的具体地址
B+树: B+树的非叶结点值只有索引,而B树每个结点中的每个关键字都有卫星数据(也就是具体数据);这就意味着同样的大小的磁盘页B+树可以容纳更多节点元素,就会显得更加“矮胖”,高度更小,磁盘操作也就相对会更少一些。
同时由于B+树增加了一个最小关键字的根结点,所以顺序访问更加便捷。
总结
数据量很大的查找,是不能直接放入内存的,而是需要什么数据就通过磁盘IO去获得。
红黑树,AVL树等二叉查找树虽然效率高,但是树的高度也大,每次访问结点都需要一次IO;而B树B+树这种多路查找树可以使得树的高度变小。
在最坏的情况下,一次IO就只能获得一个结点的值,所以在最坏的情况下,不管是红黑树还是AVL树、B树、B+树,他们对应的磁盘操作是树的高度。
关于B树和B+树的其他总结:
参考:https://blog.csdn.net/bengshakalakaka/article/details/89255902