Solr使用TrieTree对区间查询进行加速优化。
- TrieTree帮助加速区间查询的原理
以十进制区间查询为例,假设我们有421,423...642,644等数据,每个数据都对应了若干个实例,如果我们想统计423~642的总实例数量,最常想到的方法是遍历423到642每个书,并将实例个数相加。但这样做如果数据范围很大,那么累加的次数会很大。而如果要做m次这样的统计,那么运算量会是m倍。
这样看要提升效率,我们应该试图降低每次统计的累加次数。如果使用TrieTree对数据进行预处理,可以显著降低每次统计的累加次数。
首先以十进制数位(个十百千万)进行TrieTree变形得到如下树:
其次,上层树生成式,要将下层节点的累加数缓存到这层各自节点。比如,假设421有3个实例,423有2个实例,那么42这个节点要将3+2=5缓存到自身。
假设节点k的实例个数,我们记为N(k), 这样一来,如果我们要计算423~642的实例总数,我们只要做如下运算N(423)+N(44)+N(5)+N(63)+N(641)+N(642),这样从11次累加降到了6次累加。
- 对于solr应用:
上面这个例子是按照“个十百千”对数据进行TrieTree转换,solr是以二进制进行转换,可以设置参数precisionStep。举个例子,假设三个数(二进制)11001000,11000100,11011100, 11011101,如果precisionStep=4,那么转化为TrieTree为:
如果我们要查询第2~4号数的对应的实例的数量,那么计算N(1101)+N(11000100)。
那么我们怎么知道要将N(1101)和N(11000100)相加呢,其实这就是solr在searchtime时的一个算法,有一篇博文总结的很好:
查找的过程(precisionStep = 8),注意开区间和闭区间的设定:
1、将查找的范围A~B的上下界A、B值,取出最高8位,标记为A1、B1,到第一段(Tree的第一层)找在(A1~B1)内的term,得到需要查找的termlist1
2、继续取A、B值的最高16位,标记为A2、B2,到第二段来查在(A2~A1 11111111]和[B1 11111111,B2)范围内的Term,得到termlist2
3、继续取A、B值的最高24位,标记为A3、B3,到第三段来查在(A3~A2 11111111]和[B2 11111111,B3)范围内的Term,得到termlist3
4、继续取A、B值的最高24位,也即A、B值,到第四段来查找[A~A3 11111111]和[B3 11111111,B]范围内的Term,得到termlist4
5、最后查询这些term,归并,就得到了符合查询条件的docid了。从上面的描述,我们可以看到,需要查询的term最多为254+255*2+255*2+256*2=1786个,传统的方式A~B个term要小的多,因此性能有很大的提升。