最近一直在做lucene搜索模块,遇到搜索慢,网上收集一些关于优化的文章总结如下:

1、对于按创建时间的排序可以使用doc.id的方式
      new SortField(null, SortField.DOC, reverse)排序方式尽量使用INT类型的字段
     也就是按照写入索引的顺序排序
2、对于时间字符串的排序可以转换成整数进行排序
3、去掉不必要的parse
    使用TermQuery替换

    (注意:前提是该字段在建索引的时候采用不分词方式,如果在建索引时是分词方式,则会导致检索不到任何结果的问题)
4、TermQuery和Term可以只保留一个实例
      createTerm(text)

     (适用于对一个字段进行多关键词搜索的情况)
5、减少Doc到model的转换
     索引出来String到Date的转换多余而且费时
     直接使用Doc对象包装成JSONObject
6、MultiFieldQueryParser改成自己用boolean查询重构
7、减少请求参数的包装类
8、搜索排序方法可以作为常量
     将sort参数变成int型,使用swich进行判断
10、使用HitCollector类来适应不同情况下,Hits的大小
       新、旧接口
       相关搜索接口

       (如果项目中需要进行分组统计,实现HitCollector类就可以做到)
11、使用尽可能快的磁盘IO
12、日志,先写文件,每天批量入库
13、增量索引使用reopen
       新的reopen()方法只会加载那些变更过的索引片断,而不是重新加载完整的索引。

       (这在见面的日志里已有介绍,主要是尽量使用一个IndexSearcher实例)
14、setMergeFactor 在做实时索引的时候,可以设置的小一点
       这样就会及时索引进去

高亮显示优化:
   1 、问题的来源
  
  增加分词以后结果的准确度提高了,但是用户反映返回结果的速度很慢。原因是, Lucene 做每一篇文档的相关关键词的高亮显示时,在运行时执行了很多遍的分词操作。这样降低了性能。
  
  2 、解决方法
  
  在 Lucene1.4.3 版本中的一个新功能可以解决这个问题。 Term Vector 现在支持保存 Token.getPositionIncrement() 和 Token.startOffset() 以及 Token.endOffset() 信息。利用 Lucene 中新增加的 Token 信息的保存结果以后,就不需要为了高亮显示而在运行时解析每篇文档。通过 Field 方法控制是否保存该信息。修改 HighlighterTest.java 的代码如下:
  

// 增加文档时保存 Term 位置信息。 
  
  private void addDoc(IndexWriter writer, String text) throws IOException 
  
  { 
  
  Document d = new Document(); 
  
  //Field f = new Field(FIELD_NAME, text, true, true, true); 
  
  Field f = new Field(FIELD_NAME, text , 
  
  Field.Store.YES, Field.Index.TOKENIZED, 
  
  Field.TermVector.WITH_POSITIONS_OFFSETS); 
  
  d.add(f); 
  
  writer.addDocument(d); 
  
  } 
  
  // 利用 Term 位置信息节省 Highlight 时间。 
  
  void doStandardHighlights() throws Exception 
  
  { 
  
  Highlighter highlighter =new Highlighter(this,new QueryScorer(query)); 
  
  highlighter.setTextFragmenter(new SimpleFragmenter(20)); 
  
  for (int i = 0; i < hits.length(); i++) 
  
  { 
  
  String text = hits.doc(i).get(FIELD_NAME); 
  
  int maxNumFragmentsRequired = 2; 
  
  String fragmentSeparator = "..."; 
  
  TermPositionVector tpv = (TermPositionVector)reader.getTermFreqVector(hits.id(i),FIELD_NAME); 
  
  // 如果没有 stop words 去除还可以改成 TokenSources.getTokenStream(tpv,true); 进一步提速。 
  
  TokenStream tokenStream=TokenSources.getTokenStream(tpv); 
  
  //analyzer.tokenStream(FIELD_NAME,new StringReader(text)); 
  
  String result = 
  
  highlighter.getBestFragments( 
  
  tokenStream, 
  
  text, 
  
  maxNumFragmentsRequired, 
  
  fragmentSeparator); 
  
  System.out.println("/t" + result); 
  
  } 
  
  }


  
  最后把 highlight 包中的一个额外的判断去掉。对于中文来说没有明显的单词界限,所以下面这个判断是错误的:
  
  tokenGroup.isDistinct(token)
  
  这样中文分词就不会影响到查询速度了。

(本人也在项目中尝试了,可是发现总报NullPointerException错误,但看到paoding分词作者qieqie在给定的例子里也是采用优化后的方法来高亮显示,运行它的例子也没有问题,不知是不是我的项目里缩小搜索结果集,限制最大搜索1000条记录的原因导致,至今未解)