基于guava的数据组织模型

1 概述

树结构是目前索引的常用实现结构,可以大大缩减比对操作的次数。同时,如果节点间记录了前后遍历的节点,那么按此顺序查询的结果是有序的。树的高度决定了插入和查询的效率。为了保证树的高度,数据结构在二叉树的基础上提出了平衡二叉树,B-+树。它们都希望能够降低树的高度,减少比对次数,提升查找效率。
本文采用guava的TreeMultiset作为基础数据结构,实现了内存索引。

2 构建索引

索引的构建过程,包含两步操作:1找到所在位置,2 保持树的平衡。
如图当Data6加入索引时,需要比对两次,找到自己的节点位置。然后在节点上添加指向Data6的引用i6.
如果添加新的节点引起左右子树的高度发生变化,则需要进行树的高度判断和调整,保证左右子树的高度平衡。这里不介绍平衡算法。
在这里插入图片描述
基于guava的TreeMultiset特性,插入一条数据需要进行三次定位:第一次,在TreeMultiset的结构上添加节点。但是,如果有重复数据,TreeMultiset只记录第一个数据的信息。在TreeMultiset看来,如果键值比对相等,那么这两个元素就应该是一致的,所以采用了压缩存储:只存放第一个插入的元素。在遍历的时候,根据计数多次返回第一次插入的元素。为了支持键值冲突的情况,本人将元素再次封装,自己维护冲突的列表。因此需要定位到此节点,手动维护冲突元素列表。第二次和第三次定位是用head和tail找到节点。
索引的存储空间占用也是需要注意的一点。基于上面的实现方案,每个节点都需要创建一个对象。对象内部包含一个list,而list的每个元素其实是指向实际元素的指针。假如数据量为n,不同的键值为m,那么需要m个包装对象以及n个指针占用的空间。包装对象包括本身的类信息和list属性的大小,指针大小为8字节(64位操作系统)。

3 查询比对

假设我们的查询场景是age属性在[20,40]所有数据。
在使用索引的情况下,需要进行两次定位操作。下限的定位,需要进行两次比较;上线的操作需要三次比较。然后依次返回上下限之间的所有数据。
在这里插入图片描述

如果不使用索引,直接遍历所有的数据。每个数据都需要进行上下限的比较,5个数据就需要十次比较。
在这里插入图片描述

假设节点数为n,那么索引的最差比较次数为:2* log2(n).而直接寻找的比对次数是:2n。

4 查询细节

实际的情况要比上例子复杂得多。用户会针对多个字段建立索引,同时查询时可能使用索引的部分属性,查询的条件也不仅仅限于eq。
比较器能够根据索引的属性以及查询条件,定义上下限的范围。根据范围可以得到元数据集的一个子集。再遍历子集,通过所有的检索条件进行遍历。
将查询分为三种情况:EQ,GT/GE,LT/LE
EQ:在此情况下,所有的条件都能和索引的条件匹配,且都是相等查询。如图,三个condition的属性分别对应比较器的property。根据我们的设计,符合条件的所有数据都集中在树形结构的一个节点上,我们只需要找到这个节点即可。在实际实现中,通过tail和head两个操作,锁定节点。这种查询的效率非常高,只是执行了两次查找,每次查找的最坏情况是树的高度。
GT/GE:在这种情况下,按比较器的属性顺次比对condition,最先不为eq的查询条件为GT/GE 。此时能够进行比较的查询条件在此终止。tail操作的边界为(condition1,condition2,condition3),head操作的上线为(condition1,condition2,TOP_BOUNDER)。其中TOP_BOUNDER是假定的一个最大值,任何数据都比他小。返回的数据集合,不能保证所有的数据都满足检索条件,需要挨个遍历执行所有的查询条件进行二次过滤。从性能上来说,在EQ的基础上,需要对每个元素再进行所有检索属性的比对,增加了额外的性能消耗。但是如果能够定位的数据集合很小,那么这部分计算消耗是很有限的。总的来说,性能的提升取决于能够圈定的子集范围,越小性能越高。
LT/LE: 这种情况和GT/GE类似,第一个不为EQ的匹配条件为LT/LE。heads上线为(condition1,condition2,condition3),tail的下限为(condition1,condition2,BOTTOM_BOUNDER)。其中BOTTOM_BOUNDER为假定的最小值,每个元素都比这个值大。在性能消耗上,和GT/GE类似,取决于过滤的子集大小。

在这里插入图片描述

5 索引效率

从查询的效率来看,在树上确定的子集范围,决定了查询的效率。子集越小,效率越高。另外,数据的冲突率也很大程度上影响效率:例如一个节点上的元素个数占据了所有元素的一半,那么只要匹配到这个节点,就需要遍历至少一半的数据。因此索引数据要一定程度的分散。
在插入的角度来看,树的高度是最重要的影响因素。平衡二叉树会保证树的高度相对平衡,在最佳的完全二叉树情况下,上亿的数据只需要30层以内的高度。
guava的TreeMultiset支持了计数统计,分别记录了当前节点冲突的元素个数,当前节点为根节点的子树所有元素个数,当前节点为根节点的子树节点个数。所以对于计数来说,可以认为效率为1.
在iterator遍历的时候,效率和list类似。TreeMultiset本身的每个节点都记录了前后节点的引用,所以效率上和列表的类似。

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值