Java Lucene:提高未分级搜索性能?

Java Lucene:提高未分级搜索性能?,java,performance,search,lucene,Java,Performance,Search,Lucene,我正在使用Lucene 5.5.0进行索引。以下标准描述了我的环境: 索引文档由8个字段组成。它们对于语料库中的所有文档都是相同的。所有文档都具有相同的模式。 所有字段都是字符串或长字段,因此不需要进行文本分析。所有这些都由lucene存储。字符串的最大长度为255个字符。 索引主要被视为读取,90%的请求是并发读取。我在应用程序级别进行锁定,这样Lucene就不必担心并发读写。 在搜索语料库时,我不需要对结果进行任何排序。检索到的文档结果的顺序可以完全任意。 查询通常是布尔、正则表达式和数值范

我正在使用Lucene 5.5.0进行索引。以下标准描述了我的环境:

索引文档由8个字段组成。它们对于语料库中的所有文档都是相同的。所有文档都具有相同的模式。 所有字段都是字符串或长字段,因此不需要进行文本分析。所有这些都由lucene存储。字符串的最大长度为255个字符。 索引主要被视为读取,90%的请求是并发读取。我在应用程序级别进行锁定,这样Lucene就不必担心并发读写。 在搜索语料库时,我不需要对结果进行任何排序。检索到的文档结果的顺序可以完全任意。 查询通常是布尔、正则表达式和数值范围查询的组合。 在搜索语料库时,检索与查询匹配的所有文档是最高优先级。 我实现的当前搜索方法(包装Lucene的API)如下所示:

public Set<Document> performLuceneSearch(Query query) {
        Set<Document> documents = Sets.newHashSet();
        // the reader instance is reused as often as possible, and exchanged
        // when a write occurs using DirectoryReader.openIfChanged(...).
        if (this.reader.numDocs() > 0) {
            // note that there cannot be a limiting number on the result set.
            // I absolutely need to retrieve ALL matching documents, so I have to
            // make use of 'reader.numDocs()' here.
            TopDocs topDocs = this.searcher.search(query, this.reader.numDocs());
            ScoreDoc[] scoreDocs = topDocs.scoreDocs;
            for (ScoreDoc scoreDoc : scoreDocs) {
                int documentId = scoreDoc.doc;
                Document document = this.reader.document(documentId);
                documents.add(document);
            }
        }
        return Collections.unmodifiableSet(documents);
}

考虑到我的上述环境,有没有办法更快/更好地完成这项工作?特别是考虑到我不需要任何排名或排序,而是需要结果的完整性,我觉得应该有一些捷径来加快搜索速度。

你可以做一些事情来加快搜索速度。 首先,如果你不使用评分,你应该禁用标准,这将使指数更小。 由于您只使用StringField和LongField,而不是使用带有关键字标记器的TextField,因此这些字段的规范被禁用,因此您已经得到了该字段

第二,你应该构造和包装你的查询,这样你就可以最小化对实际分数的计算。也就是说,如果使用BooleanQuery,请使用occure.FILTER而不是occure.MUST。两者都有相同的包含逻辑,但过滤器不得分。对于其他查询,考虑将它们封装在常数项中。然而,这可能根本没有必要,下面的解释如下

第三,使用自定义收集器。默认的搜索方法适用于小的、排序的或排序的结果集,但是您的用例不适合这种模式。下面是一个示例实现:

import org.apache.lucene.document.Document;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.SimpleCollector;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


final class AllDocumentsCollector extends SimpleCollector {

  private final List<Document> documents;
  private LeafReader currentReader;

  public AllDocumentsCollector(final int numDocs) {
    this.documents = new ArrayList<>(numDocs);
  }

  public List<Document> getDocuments() {
    return Collections.unmodifiableList(documents);
  }

  @Override
  protected void doSetNextReader(final LeafReaderContext context) {
    currentReader = context.reader();
  }

  @Override
  public void collect(final int doc) throws IOException {
    documents.add(currentReader.document(doc));
  }

  @Override
  public boolean needsScores() {
    return false;
  }
}
字段名可以相同,如果完全依赖DocValues,则可以选择不存储其他字段。 必须更换收集器,例如一个字段:

import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.SortedDocValues;
import org.apache.lucene.search.SimpleCollector;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


final class AllDocumentsCollector extends SimpleCollector {

  private final List<String> documents;
  private final String fieldName;
  private SortedDocValues docValues;

  public AllDocumentsCollector(final String fieldName, final int numDocs) {
    this.fieldName = fieldName;
    this.documents = new ArrayList<>(numDocs);
  }

  public List<String> getDocuments() {
    return Collections.unmodifiableList(documents);
  }

  @Override
  protected void doSetNextReader(final LeafReaderContext context) throws IOException {
    docValues = context.reader().getSortedDocValues(fieldName);
  }

  @Override
  public void collect(final int doc) throws IOException {
    documents.add(docValues.get(doc).utf8ToString());
  }

  @Override
  public boolean needsScores() {
    return false;
  }
}
您将分别对长字段使用getNumericDocValues。当然,您必须在同一个收集器中对所有必须加载的字段重复此操作,最重要的是:测量何时最好从存储的字段加载完整文档,而不是使用DocValues

最后一点注意:

我在应用程序级别进行锁定,这样Lucene就不必担心并发读写


IndexSearcher和IndexWriter本身已经是线程安全的。如果您只为Lucene锁定,您可以移除这些锁定,并在所有线程之间共享它们。并考虑使用Oal.Sq.SeCelMeCar来重用索引阅读器/搜索器。

< P> >有两件事可以做,以加快搜索速度。 首先,如果你不使用评分,你应该禁用标准,这将使指数更小。 由于您只使用StringField和LongField,而不是使用带有关键字标记器的TextField,因此这些字段的规范被禁用,因此您已经得到了该字段

第二,你应该构造和包装你的查询,这样你就可以最小化对实际分数的计算。也就是说,如果使用BooleanQuery,请使用occure.FILTER而不是occure.MUST。两者都有相同的包含逻辑,但过滤器不得分。对于其他查询,考虑将它们封装在常数项中。然而,这可能根本没有必要,下面的解释如下

第三,使用自定义收集器。默认的搜索方法适用于小的、排序的或排序的结果集,但是您的用例不适合这种模式。下面是一个示例实现:

import org.apache.lucene.document.Document;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.SimpleCollector;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


final class AllDocumentsCollector extends SimpleCollector {

  private final List<Document> documents;
  private LeafReader currentReader;

  public AllDocumentsCollector(final int numDocs) {
    this.documents = new ArrayList<>(numDocs);
  }

  public List<Document> getDocuments() {
    return Collections.unmodifiableList(documents);
  }

  @Override
  protected void doSetNextReader(final LeafReaderContext context) {
    currentReader = context.reader();
  }

  @Override
  public void collect(final int doc) throws IOException {
    documents.add(currentReader.document(doc));
  }

  @Override
  public boolean needsScores() {
    return false;
  }
}
字段名可以相同,如果完全依赖DocValues,则可以选择不存储其他字段。 必须更换收集器,例如一个字段:

import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.SortedDocValues;
import org.apache.lucene.search.SimpleCollector;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


final class AllDocumentsCollector extends SimpleCollector {

  private final List<String> documents;
  private final String fieldName;
  private SortedDocValues docValues;

  public AllDocumentsCollector(final String fieldName, final int numDocs) {
    this.fieldName = fieldName;
    this.documents = new ArrayList<>(numDocs);
  }

  public List<String> getDocuments() {
    return Collections.unmodifiableList(documents);
  }

  @Override
  protected void doSetNextReader(final LeafReaderContext context) throws IOException {
    docValues = context.reader().getSortedDocValues(fieldName);
  }

  @Override
  public void collect(final int doc) throws IOException {
    documents.add(docValues.get(doc).utf8ToString());
  }

  @Override
  public boolean needsScores() {
    return false;
  }
}
您将分别对长字段使用getNumericDocValues。当然,您必须在同一个收集器中对所有必须加载的字段重复此操作,最重要的是:测量何时最好从存储的字段加载完整文档,而不是使用DocValues

最后一点注意:

我在应用程序级别进行锁定,这样Lucene就不必担心并发读写


IndexSearcher和IndexWriter本身已经是线程安全的。如果您只为Lucene锁定,您可以移除这些锁定,并在所有线程之间共享它们。考虑使用Oal.Sq.SeCelMeCar来重用索引阅读器/搜索器。非常感谢,我真的很感激!我一定要去
我想尝试一下这些建议。我需要应用程序本身的锁定功能,所以我没有特别为Lucene实现它,但是很高兴知道这些类本身是线程安全的。最后一个问题:使用DocValuesFields而不是存储字段需要更改持久文件格式,对吗?因为我已经在几个站点部署了我的代码,某些人会对破坏更改感到不高兴。是的,使用DocValues需要更改索引,从而有效地进行完全的重新索引。不过,DocValues可以添加到索引中,不需要删除任何内容,因此我可以想象一个迁移脚本可以在零停机的情况下完成这项工作。总之,有更多的理由对此进行彻底测试,并权衡性能与维护的好处。哇,这是一个全面的答案!非常感谢,我真的很感激!我一定会尝试一下这些建议。我需要应用程序本身的锁定功能,所以我没有特别为Lucene实现它,但是很高兴知道这些类本身是线程安全的。最后一个问题:使用DocValuesFields而不是存储字段需要更改持久文件格式,对吗?因为我已经在几个站点部署了我的代码,某些人会对破坏更改感到不高兴。是的,使用DocValues需要更改索引,从而有效地进行完全的重新索引。不过,DocValues可以添加到索引中,不需要删除任何内容,因此我可以想象一个迁移脚本可以在零停机的情况下完成这项工作。总而言之,更有理由对此进行彻底测试,并权衡性能与维护的好处。