当前位置: 首页 > 科技观察

腾讯面试官用“B+树”来折磨我

时间:2023-03-21 19:52:00 科技观察

我们知道,当系统要处理的数据量非常大的时候,不可能把所有的数据都存储在内存中,需要使用磁盘完成存储和检索。数据库支持多种索引方式,如哈希索引、全文索引、B+树索引等。今天给大家分享一下使用B+树作为索引的优缺点。很多互联网公司在面试的时候都会问这个问题。可能我们看的面谈太多了,但是答案基本都是一样的,面试官基本听腻了。我多么希望听到不同的答案。那么希望今天这篇文章能够给大家一个不一样的答案。第一节和第二节,我们来问问邓哥,他是不是傲娇。回到正题,今天分享的要点如下:1、从磁盘读写数据和从内存读写数据有什么区别?我们通常有机械硬盘和固态硬盘。内存是一种半导体器件。对于内存来说,知道内存地址就可以通过地址获取数据,这是内存的随机访问特性。访问速度快但价格昂贵,所以内存空间一般都比较小。对于磁盘,它是一种机械设备。每当磁盘访问数据时,都需要等待磁盘盘片旋转到磁头,然后才能读取相应的数据。尽管磁盘旋转的很快,但是和内存的随机存取相比还是很渣的。可以看到,如果是随机读写,性能差距非常大。那么如果顺序访问大量数据,磁盘和内存的性能其实相差不大。为什么是这样?磁盘的最小读写单位是扇区。现在一个磁盘扇区一般是4k字节。对于操作系统来说,一次会读取多个扇区,目前为止操作系统的最小读取单位是块。每当我们从磁盘读取一段数据时,操作系统会一次性读取整个块,所以对于大量的顺序读写,磁盘效率会比随机读写高很多。假设现在你有一个有序的数组,所有的数组都以块的形式存储在磁盘上,现在我们通过二分查找的方式来查找元素A。首先我们找到中间的元素,将其从块中取出,从磁盘中调入内存,然后在内存中进行二分查找。在进行下一步时,如果查找到的元素在另一个块中,我们需要继续从磁盘读取到内存中。这样反复从磁盘到内存的效率会很低。所以我们需要找到一种方法来保持尽可能低的磁盘访问次数。2数据与索引的分离我们以公安系统为例。系统中有很多用户。每个用户除了姓名、年龄等基本信息外,还有一个唯一的ID。拿到这个ID我们就可以知道对应的基本信息了。但是,每个用户的基本信息太多,无法存储在内存中,所以考虑存储在磁盘中。用户数据是有序数组的形式,里面存放的是用户ID和用户信息在磁盘上的位置,所以我只需要存放两个元素,直接存放在内存中。有序数组如下图所示,但是在数据频繁变化的场景中,有序数组的缺点就显现出来了。在大多数情况下,请考虑使用二叉搜索树或哈希表。但是哈希表不支持区间查询,所以更多的是使用二叉搜索树。如下图所示,这里插入图片说明。如果索引太多,内存中还是存不下,是否可以考虑将索引存入磁盘?如何高效地组织磁盘上的索引结构?这就介绍了B+树2B+树使得节点大小等于块大小我们知道,操作系统在访问磁盘时,通常是以块为单位进行读取的。如果你当前需要读取的数据只有几个字节,但是磁盘还是会把整块都读出来,读写效率是不是很低?在B+树中,大哥用一个节点的大小等于一个块的大小。节点存储的不是元素,而是有序数组。这样就充分利用了操作系统的例程,最大限度地提高了读取效率。节点和叶节点内部节点和叶节点虽然结构相同,但存储的内容不同。内部节点存储key和指针来维护树结构,但不存储key对应的数据。叶子节点存储键和对应的数据,不存储指针来维护树结构,最大限度地利用了节点空间。内部节点和叶子节点B+树使用双向链表,具有良好的范围查询能力和灵活的调整能力。总结以上三点,B+树是一棵完全平衡的m阶多叉树。m阶多叉树3B+树的搜索方案刚刚吹了一波B+树,它是怎么搜索的?具体的查找过程是这样的:我们首先确认要查找的查询值,以及它位于数组中相邻两个元素之间,然后我们读出第一个元素对应的指针,从而得到下一个元素的位置堵塞。读取下一个区块的节点数据后,我们对其进行同样的操作。这样B+树就会逐层访问内部节点,直到叶子节点被读出。对于叶子节点中的数组,我们可以直接使用二分查找算法来判断要查找的元素是否存在。如果存在,我们就可以得到查询值对应的存储数据。如果这个数据是详细信息的位置指针,那么我们就需要再次访问磁盘来读取详细信息。B+树是一棵m阶的多叉树,所以B+树中的一个节点可以存放m个元素的数组。好吧,这样的话,只需要几层b+树就可以对数据量很大的数字进行索引。比如一个2k的节点可以存储200个元素,那么一个4层的B+树可以存储200^4,也就是16亿个元素。如果只有四层,意味着我们最多可以访问磁盘4次。假设当前每个节点2k,那么第一层的一个节点只有2k,第二层的节点最多有200个元素,一共0.8M。第三层是200^2,也就是40000个节点,一共80M。对于现在的电脑来说,我们可以把前三层存到内存里,只需要把第四层存??到磁盘里,这样分手前只需要和磁盘打交道一次,这就是面试想要的知道。分为内部节点和叶节点。4如何动态调整B+树上面介绍了B+树的结构和查询原理。下面我们看看B+树是如何增删改查的。下面我们以三元素的B+树为例。假设我们现在要插入ID。对于6=5的元素,第一步是找到对应的叶子节点。如果叶子节点没有满,可以直接插入元素6。如果我们插入的元素是10?按理来说,我们应该在9之后插入,但是节点已经满了,所以我们需要走另一条路。方法是分裂这个叶子节点,也就是生成一个新的节点,然后平分两个节点之间的数据。节点分裂是显而易见的。叶节点的分裂影响父节点。如果父节点也满了,分裂节点分裂也是必要的。总结把大问题拆分成小问题,一个一个解决,是我们在生活中学到的一项重要技能,相对复杂。B+树实际上是由基本数据结构(数组、链表、树)组成的,它的检索过程实际上是二分查找,所以如果B+树完全加载到内存中,其检索效率堪比有序数组/二叉搜索树几乎相同,但更复杂。B+树最大的优点是将索引存储在磁盘上,使检索技术摆脱了内存的限制。此外,通过将索引与数据分离,索引数组的大小保持在一个较小的范围内,从而简化了索引。本文转载自微信公众号《我是程序员》,可关注下方二维码。转载本文请联系我程序员小健公众号。