Lucene性能优化之使用Lucene预排序加速搜索速度

                                                                                                             

      简单说搜索个过程,Lucene 在搜索的程序简单分成两部分,搜索与排序。高手请忽略下面我的讲解。

  搜索:

   通过倒排的索引找到对应的 docId 集,拿出来,通过 Collector 类的实现类中的 collect(int docId )

          就可以拿到了。

  排序:

   因为拿出来后,还有一个排序的过程,现在我们只讨论按某一个数字字段进行排序,Collector 的实现类都会从一个叫 FieldCache 类中,加载了需要排序的字段的一个 数组的值,数组下标为docId, 然后通过 FieldValueHitQueue 这一个类进行排序了。

 

  象一般来讲,做搜索应用都会按时间从远到近,一段一段地把数据存入到索引中,完成后,如有新增的数据,只需要把新增那部分存入索引则可,这是很普遍的做法。

  完成后似乎一切很顺利,哦,对了,还有一个事情就是其实很多的业务应用中,都是把搜索结果按一个或者多个字段排序了,比如:把搜索出来的结果,按发布时间倒排,或者按评论数倒排,又或者一个比较常用的业务就是,出报表,把最新的前 1000 条记录按发布时间倒排出来,甚至还要按页数一页一页出来结果。这里暂不讨论按匹配度等排序的情况。

 

  问题的来源:

  因为我们入索引是从远到近的,可以简单地认为,如:发布时间(time)在索引里面都是一种升序的形式存入索引,而搜索出来后排序则是一种倒序的形式,问题就是这样子,按排序算法来说这就是最差的一种情况了,把一个升序排序成倒序,但通过研究Lucene 的索引发现,一旦达到某个数据量,索引里面的并非完全按 时间正序入索引的,而是把document 分成了几段,我想这样子就是为了避免我刚才说的排序最坏的情况出现吧...但只能是治标不治本的做法,仅是折衷的做法。

 

    解决思路:

      现在公司的搜索业务方面的速度不快,估计数据量比较大的原因,整一个集群的速度我个人并不满意。

   在 lucene 的排序中,其实仅仅搜索的速度是挺快的,主要的性能瓶颈在排序那里,然后我深入再发现了解到,公司的搜索业务中很多都使用发布时间倒序完成的,这自然让我产生一种想法,为什么不预先让他们排序好?因为既然是按发布时间倒序完成的,那说明只要命中docId ,则它的排序已经固定下来了。

  1.  在加载索引的时候,通过 termEum 先把某一个时间段排序后,得到一个已经排序好的数组了。

  2.  自己另外再写一个 Collector 的实现类,里面只需要记录命中的 docId,

  3.  完成后,再遍历那个已经排序好的数组,拿出 docId ,看是不是已经命中的,拿够了前 20 条再 break 出来就完成整一个搜索与排序的过程了,

 

  从上面看到,都已经没有排序的过程,因为已经第一步实现了排序。代码我不再演示了,思路已经写在那里了,代码估计对于各路大神也不是什么难事。酷

  下面给出测试的条件与结果:

   索引的结构给一下,只需要留意红框里面的字段就可以了

Lucene性能优化之使用Lucene预排序加速搜索速度_第1张图片

      

   我分了三次测试条件, 1000万,3000万, 总数,其中 is 字段是为了可以均匀地把数据划分成 10 份。都没有使用分词器,只对数字进行索引。

 

 

        第一次测试

   1000W 数据,按 is 字段,搜索 10 平均得出每一次搜索结果约 300W.按 time 字段倒序,同样条件,对比预排序与lucene 自身排序的速度比较

        Lucene性能优化之使用Lucene预排序加速搜索速度_第2张图片

            从结果可以看出,使用预排序的方式后只需要用Lucene 自带排序的 30% 的时间 , 平均每一次搜索结果约 100W 数据,(上面的预排序那里是 44.922 's 我是整理时错误了, sorry .... )

 

 

          第二次测试

   3000W 数据,按 is 字段,搜索 10 平均得出每一次搜索结果约 300W.按 time 字段倒序,同样条件,对比预排序与lucene 自身排序的速度比较

          Lucene性能优化之使用Lucene预排序加速搜索速度_第3张图片

           从图上可以看出,同样道理搜索出来的结果,平均每次300W,而且搜索结果数完全一致,排除加载缓存及其他因素,预排序也只需要Lucene 普通排序的 30% 的时间就完成了

 

          第三次测试

         这次比较极端,使用MatchAllQuery 进行排序,就是 1000W 结果参与排序,对于Lucene自带的那种排序方式最致命,而对于预先排序这种方式则是最优的方式。

       Lucene性能优化之使用Lucene预排序加速搜索速度_第4张图片

        从上面看到, 1000W 的数据参与排序,Lucene自带的排序方式,用了近 1秒的时间去完成排序,而预排序则只用了 0.3 秒就完成了。

  

 

        总结论:

 

    通过上面的测试,使用预排序的方式,速度提升很明显,为Lucene自带排序的 30 % 时间,对整体的搜索业务性能是一个很大的提升。同时在这里可以扩展一下,通过这样子的预先排序,还可以扩展为,对数字的搜索,或数字区间搜索,而不是使用 NumericQuery ,速度会更快平均在 30ms 的搜索用时,再再再扩展一下,还可以对数字或者数字区间进行统计,比 Solr 的自带的统计速度还要快不少!我稍候还会针对这些做一些扩展开发,请大家稍等。

   缺点: 我觉得,可能是每一次加载索引,或者切换新索引的时间,需要用1分钟左右,进行一个预排序的过程,但我认为相对于优点,这一个缺点不大,一般都是通过加载完新的索引并构造好预排序后,再切换到新索引,问题不大。

   如果对于那些更新并不频繁而数据量特别大如: 半小时更新一次等,我觉得很适合使用这种方法提升性能,而对于那些几分钟就更新一次的搜索业务,我觉得,这种方式就慎用了,毕竟每次这样子预排序一次,对性能伤害很大。

                                                                                          by [email protected]

 

           欢迎转载: http://kernaling-wong.iteye.com/blog/1997365 请注明作者

你可能感兴趣的:(Lucene)