具体来说,我们设计了一个完全动态的学习指数,即分片几何模型指数(PGM-index),它根据一个新的递归结构中固定的最大误差容忍度来协调线性模型的最佳数量(第2和第3节)。根据[32]所证明的下限,PGM-index以I/O优化的方式解决了前身搜索问题,同时占用了简洁的空间。此外,它的新设计使它成为一个完全学习的索引(不像RMI和FITing-tree那样混合了传统和学习的设计元素),它使我们能够引入新的技术,使其压缩(第4节),不仅对密钥分布,而且对查询分布也有适应性(第5节)。然后,我们表明PGM-索引可以根据任何给定的空间或延迟要求有效地自动调整自己(第6节)。
我们在高达10亿个密钥的合成和真实世界数据集上测试了PGM-index的效率(第7节)。简而言之,PGM-index的实验成果是。(i) 比FITing树[17]的空间占有率高75%,比CSS树[33]的空间占有率高83倍,查询时间相同或更好;(ii) 在查询时间和空间占有率方面统一改进了RMI[22]的性能,构建速度快15倍,同时不需要调整超参数;(iii) 在各种动态工作负载中,查询和更新时间比B+树高71%,同时空间占有率降低四个数量级(从数千字节到数兆字节)
假设S是一个从宇宙U中抽取的n个键的多集,2 PGM-index是一个以整数ε≥1为参数的数据结构,它解决了S上的完全可索引字典问题,如第1节所定义。
让A是一个排序的数组,存储S的(可能是重复的)键。PGM-index的第一要素是一个分片线性近似模型(PLA-model),即U中的键和它们在数组A中的近似位置之间的映射。具体来说,我们的目标是学习一个映射,返回一个键k∈U的位置,该位置与A中的正确位置最多相差ε。我们说分片线性,是因为一个单一的线性模型(也称为段)可能不足以ε-近似U中所有键的位置。因此,PGM-index学习了一连串的段,每个段需要恒定的空间(两个浮点和一个键)和恒定的查询时间来返回K在A中的ε-近似位置。我们在下面的Lemma 1中表明,存在一个线性时间和空间算法来计算最佳的PLA模型,即由最少的ε-近似段组成的模型。我们还注意到,由最优PLA模型返回的ε-近似位置可以通过二进制搜索在A中的±ε键范围内变成精确位置,因此需要的时间是参数ε的对数,与n无关。
总的来说,每个PLA模型构成PGM指数的一个层次,而该PLA模型的每个片段构成该层次的数据结构的一个节点。这种递归结构相对于已知的学习指数建议(参见FITing-tree或RMI)的特殊性在于PGM-指数是一个纯粹的学习指数,在其结构中(如FITing-tree)或在ML模型错误过多时(如RMI)不依赖于经典数据结构。其净结果是影响其时空复杂性的三个主要优势。首先,PGM索引使用线性模型(即段)作为数据结构各级的恒定空间路由表,而其他索引(如FITing-tree、B-tree和变体)使用的是存储大量键的空间消耗型节点,这只取决于磁盘页的大小,因此导致对数据分布中可能存在的规律性视而不见。第二,PGM-index的这些路由表需要恒定的时间来将节点中的键的搜索限制在一个较小的键子集(大小为2ε),而B+树和FITing树中的节点会产生一个随节点大小而增长的搜索成本,从而在查询操作中减慢了树的遍历。第三,在本文中,我们观察到计算最小段数是一个众所周知的计算几何问题,它在线性时间和空间上都有一个最优解,因此超过了FITing-tree和RMI的次优方案。
下面两个小节详细介绍了PGM-索引的构造和查询操作,第三节讨论了插入和删除。
在这一节中,我们描述了如何有效地计算和简洁地存储从键到A中的位置的映射等级的ε-近似实现,这是PGM-索引的核心设计要素之一。
如图1所示,段s是一个三要素(键、斜率、截距),通过函数fs(k)=k×斜率+截距来索引U的范围。PGM指数的一个重要特征是其分段的 "精度 "ε。
定义 1.让A是一个从宇宙U中抽取的n个键的排序数组,让ε≥1是一个整数。一个段s = (key, slope, intercept)被称为提供了[ki, ki+r]中所有键的范围的一个ε近似索引。
我们注意到,段所提供的ε-近似索引并不仅仅适用于A中的键,而是适用于U中的所有键。 因此,一个段可以被看作是一个近似的前身搜索数据结构,其覆盖的键的范围提供了O(1)的查询时间和O(1)的占用空间。然而,一个段可能不足以对A中的整个键集进行ε-近似的等级函数;因此,我们看一下段序列的计算,也称为PLA-model。
定义2。给定ε≥1,分段线性ε-近似问题包括计算PLA模型,该PLA模型最小化其片段{s0,…,sm−1}的数量,前提是每个片段sj对于S中的键的覆盖范围是ε-近似的。这些范围是不相交的,并且一起覆盖整个宇宙U。
找到一个数组A的最佳PLA模型的方法是通过动态编程,但是它所需要的O(n3)时间是非常大的。FITing-tree[17]的作者通过一种启发式的方法来解决这个问题,这种方法在时间上是线性的,但是不能保证找到最优的PLA模型,而且实际上它的表现也很差(正如我们在第7.1节中所展示的)。
有趣的是,我们发现这个问题在时间序列的有损压缩和相似性搜索方面已经得到了广泛的研究(例如,见[28, 9, 11, 12, 39]和其中的参考文献),并且它承认流算法需要O(n)最佳时间和空间。这一系列方法的关键思想是将分片线性ε-逼近问题简化为构建点集合的凸壳问题,在我们的例子中,凸壳是i=0, …, n-1时逐渐增长的集合{(ki, rank (ki)}。, n-1. 只要凸壳可以被包围在一个高度不超过2ε的(可能是旋转的)矩形中,指数i就会被递增,并且集合被扩展。一旦包围凸壳的矩形高度超过2ε,我们就停止构建,并确定PLA模型的一个部分,即把该矩形分割成两个大小相等的部分的线。然后,当前的处理元素集被清空,算法从其余的输入点重新开始。这种贪婪的方法可以被证明在PLA模型的大小上是最优的,并且具有线性的时间和空间复杂性。在我们的背景下,我们可以把这个结果重新表述如下。
定理1(Opt. PLA-model [28])。给出一个序列{(xi, yi)}i=0,…,n-1,这些点的x坐标是不递减的。存在一种流算法,可以在线性时间和空间内计算出ε-近似于该序列中每个点的y坐标的最小段数。
对于我们在字典问题上的应用,Lemma 1中的xis对应于输入键ki,yis对应于它们在排序的输入数组A中的位置0, . . . 下一步是证明一个简单但非常有用的约束,即最佳PLA模型的一段所覆盖的键的数量,我们在分析PGM-指数时使用这个约束。
定理2. 给出一个键的有序序列ki∈U和相应的序列{(ki, i)}i=0,…,n-1,这些点在笛卡尔平面内的坐标都是不递减的。定理1的算法确定了一个(最小)的段数mopt,每个段至少覆盖2ε个点,所以最小段数mopt≤n/(2ε)。
Lemma 1的算法将输入数组A的最优PLA模型返回为m段的序列M = [s0, …, sm-1]。现在,为了解决完全可索引字典的问题,我们需要找到负责估计查询键k的近似位置的ε-近似段sj,这是最右边的段sj,使sj.key≤k。在后一种情况下,对这个数据结构的查询将需要O(logB m + log(ε/B)) I/O,其中B是多路树的扇出量,ε是近似等级(k)时一个片段产生的误差。
然而,上面的索引策略并没有充分利用键的分布,因为它采用了一个具有固定扇出的经典数据结构来索引M。因此,我们引入了一种新的策略,它包括在从片段序列中得到的一组键上递归地重复分片线性近似过程。更确切地说,我们从在整个输入数组A上构建的序列M开始,然后提取A的第一个键被每个段覆盖,最后在这个减少的键集合上构建另一个最优的PLA模型。我们以这种递归的方式进行,直到PLA模型由一个段组成,如图2的伪代码所示。
如果我们把段映射到节点上,那么这种方法就构建了一种多路搜索树,但相对于B树(因此也相对于FITing树)有三个主要优势。(i) 它的节点有可变的扇形,由与这些节点相关的段所覆盖的键的数量(通常很大)所驱动;(ii) 一个节点中的段对要支持的各种查询起着恒定空间和恒定时间的ε-近似路由表的作用;(iii) 每个节点中的搜索通过二进制搜索纠正该路由表返回的ε近似位置(见下文),因此它的时间成本取决于对数的ε,与相应段所覆盖的键数量无关。
现在,对这个递归PGM-索引的查询操作如下。在每一级,它使用指向被访问节点的段来估计被搜索的键k在下一级的键中的位置。考虑到下一层的每个键都是该层的段所覆盖的第一个键,我们已经确定了下一个要查询的段,这个过程一直持续到最后一层。图2中描述了查询操作的伪代码和一个例子。
Theorem 1.//
PGM索引的主要创新之处在于它的空间开销并不像第一节提到的传统索引那样随n线性增长,而是取决于输入数组A的 “规则性趋势”。由于这一事实对于递归层也是成立的,因此,PGM-index在空间和时间上不可能渐进地比2ε-way树,如FITing树、B+树或CSS树更差(只要在定理1中取c=2ε=Θ(B))。因此,根据文献[32]所证明的下限,我们可以说PGM-index以I/O最优的方式解决了具有前身搜索的完全可索引字典问题,这意味着它有可能在几乎没有性能下降的情况下取代任何现有索引。
表1总结了PGM指数及其竞争对手在随机存取机(RAM)和外部存储器(EM)模型中的点查询的界限。第7节的实验结果进一步支持了这些理论成果,表明PGM-index比FITing-tree、B±tree和CSS-tree更快、更简洁,因为在实践中,mopt 远小于 n和递归结构保证了c 远大于 2ε.
与传统索引相比,PGM-索引中的插入和删除更难实现。首先,一个段可以索引一个可变的、可能很大的数据子集,这一事实使得经典的B树节点拆分和合并算法不适用,因为它们确实依赖于一个节点包含固定数量Θ(B)的键的事实。我们确实可以强制分段覆盖固定数量的键,但这将瓦解PGM-索引的索引能力。现有的学习型索引建议为每个节点(模型)在一个排序的缓冲区中插入新的元素,并不时地与主索引合并,从而导致相应模型的重新训练[17, 22]。当一个模型索引了许多键(因此它的再训练很慢),或者当插入的键空间的某个区域(因此由于快速填充少数缓冲区而导致许多合并),这种解决方案是低效的。在本节中,我们提出了两种处理更新的改进策略,一种针对时间序列,另一种针对更普遍的动态场景。
如果新的键被添加到数组A的末尾,同时保持排序的顺序(就像在时间序列中发生的那样),PGM-index会在O(1)的摊销时间内更新最后一个段[28]。如果新的键k可以被这个最后的段所覆盖,同时保留了ε的保证,那么插入过程就会停止。否则,将创建一个带有密钥k的新段。然后,在上面一层的最后一个网段中递归地重复插入k的过程。当任何一层的段在ε保证范围内覆盖k,或者到达根段时,递归就会停止。在这一点上,根段可能需要拆分,并创建一个新的根节点与它相应的段。由于每一级的工作都需要恒定的摊销I/O,所以这种插入算法所需的I/O总数是O(logc m)的摊销数。、
对于发生在A的任意位置的插入,我们使用在[29,26]中提出的对数方法,在此对其进行调整以适用于学习型索引。我们定义了一系列的PGM索引,这些索引建立在键的集合S0, … , Sb的键,这些键要么是空的,要么是大小为20, 21, … , 2b,其中b =Θ(log n)。 由于Sjs是被排序的(0≤j < i),这个联合可以在与合并集的大小成线性关系的时间内被计算出来。新的排序集由2i个键组成(鉴于2i = 1 + Pi-1 j=0 2j)。新的合并集被用作Si,而之前的集合被清空。如果我们考虑一个键并检查它在n次插入中的历史,我们注意到它最多可以参与b =Θ(log n)次合并,因为每次合并都会将键移到右边的索引上,而完整的Slog n可能包括所有插入的键。鉴于合并需要的时间与被合并的键的数量成线性关系,我们在每次合并时为每个键支付O(1)的摊销时间,也就是说,每次插入的摊销时间为O(log n)。
对一个键d的删除处理与插入类似,通过添加一个特殊的墓碑值来表示对d的逻辑删除,关于细节,我们请读者参考[29]。
对于范围查询,在每个Sis中进行搜索并通过大小为b的堆进行排序合并就足够了,其成本为O(log n(log m+ logε) + K log log n)时间,其中K是满足范围查询的键的数量。如果范围查询的结果可以在O(K)空间中缓冲,那么成本就降低到O(log n(log m+logε)+K)时间。
压缩PGM-指数归根结底是为键和段(即截距和斜率)提供适当的无损压缩器,它们构成了我们学习数据结构的组成部分。在这一节中,我们提出了专门针对片段压缩的技术,因为键的压缩是一个正交的问题,存在着大量的解决方案。
关于压缩截距的问题,我们的做法如下。截距可以通过使用段的坐标系统来增加,即对于一个段sj = (key j, slope j, intercept j)计算覆盖的键k的位置为fsj (k) = (k- key j) × slope j + intercept j.然后,由于fsj (k)的结果必须被截断以返回A中的整数位置,我们将截距存储为整数bintercept jc。最后,我们利用截距小于n的事实,从而使用[25]的简洁的数据结构,得到以下结果。
命题1. 假设m是一个PGM索引的段数,该索引从宇宙U中抽取了n个键,这些段的截点可以用m log(n/m)+1.92m+o(m)位来存储,并且可以在O(1)时间内随机访问。