如何在分布式环境下提高Lucene性能?

11
我在分布式环境下使用主-分片实现进行搜索时,面临长时间的搜索时间(约10秒)。但是,通过Luke进行相同的查询可以在毫秒级内返回结果。
该应用程序是一个分布式系统。所有节点共享一个存储索引的公共NFS挂载点。为了简单起见,让我们考虑两个节点Node1和Node2。/etc/fstab条目如下。
nfs:/vol/indexes /opt/indexes nfs rw,suid,nodev,rsize=32768,wsize=32768,soft,intr,tcp 0 0

系统中存在多个数据源(例如数据源1数据源2),每个节点上都有一个对应的分片以及每个数据源对应的主节点。索引如下:

Feed1-master
Feed1-shard-Node1.com
Feed1-shard-Node1.com0
Feed1-shard-Node1.com1

搜索的代码如下:
FeedIndexManager fim = getManager(feedCode);
searcher = fim.getSearcher();
TopDocs docs = searcher.search(q, filter, start + max, sort);

private FeedIndexManager getManager(String feedCode) throws IOException {
  if (!_managers.containsKey(feedCode)) {
    synchronized(_managers) {
      if (!_managers.containsKey(feedCode)) {
        File shard = getShardIndexFile(feedCode);
        File master = getMasterIndexFile(feedCode);
        _managers.put(feedCode, new FeedIndexManager(shard, master));
      }
    }
  }  
  return _managers.get(feedCode);
}

FeedIndexManager的作用如下。
public class FeedIndexManager implements Closeable {

  private static final Analyzer WRITE_ANALYZER = makeWriterAnalyzer();
  private final Directory _master;
  private SearcherManager _searcherManager;
  private final IndexPair _pair;

  private int _numFailedMerges = 0;
  private DateTime _lastMergeTime = new DateTime();

  public FeedIndexManager(File shard, File master) throws IOException {
    _master = NIOFSDirectory.open(master, new SimpleFSLockFactory(master));

    IndexWriter writer = null;
    try {
      writer = new IndexWriter(_master,
                               WRITE_ANALYZER, 
                               MaxFieldLength.LIMITED);
    } finally {
      if (null != writer) {
        writer.close();
      }
      writer = null;
    }

    _searcherManager = new SearcherManager(_master);
    _pair = new IndexPair(_master,
                          shard, 
                          new IndexWriterBuilder(WRITE_ANALYZER));
  }

  public IndexPair getIndexWriter() {
    return _pair;
  }

  public IndexSearcher getSearcher() {
    try {
      return _searcherManager.get();
    }
    catch (IOException ioe) {
      throw new DatastoreRuntimeException(
        "When trying to get an IndexSearcher for " + _master, ioe);
    }
  }

  public void releaseSearcher(IndexSearcher searcher) {
    try {
      _searcherManager.release(searcher);
    }
    catch (IOException ioe) {
      throw new DatastoreRuntimeException(
        "When trying to release the IndexSearcher " + searcher
        + " for " + _master, ioe);
    }
  }

  /**
   * Merges the changes from the shard into the master.
   */
  public boolean tryFlush() throws IOException {
    LOG.debug("Trying to flush index manager at " + _master
              + " after " + _numFailedMerges + " failed merges.");
    if (_pair.tryFlush()) {
      LOG.debug("I succesfully flushed " + _master);
      _numFailedMerges = 0;
      _lastMergeTime = new DateTime();
      return true;
    }
    LOG.warn("I couldn't flush " + _master + " after " + _numFailedMerges
             + " failed merges.");
    _numFailedMerges++;
    return false;
  }

  public long getMillisSinceMerge() {
    return new DateTime().getMillis() - _lastMergeTime.getMillis();
  }

  public long getNumFailedMerges() {
    return _numFailedMerges;
  }

  public void close() throws IOException {
    _pair.close();
  }

  /**
   * Return the Analyzer used for writing to indexes.
   */
  private static Analyzer makeWriterAnalyzer() {
    PerFieldAnalyzerWrapper analyzer = 
      new PerFieldAnalyzerWrapper(new LowerCaseAnalyzer());

    analyzer.addAnalyzer(SingleFieldTag.ID.toString(), new KeywordAnalyzer());
    // we want tokenizing on the CITY_STATE field
    analyzer.addAnalyzer(AddressFieldTag.CITY_STATE.toString(),
            new StandardAnalyzer(Version.LUCENE_CURRENT));
    return analyzer;
  }
}

这个调用是消耗大约95-98%的延迟的罪魁祸首,一次搜索需要约20秒的时间,而如果通过Luke打开索引,则只需毫秒级别的时间。
TopDocs docs = searcher.search(q, filter, start + max, sort);

我有以下问题:
  1. 每个源是否可以有多个主机,或者我应该将其减少到仅一个主机?索引中的元素数量约为5000万。

  2. 当实体数少于100万时,反应速度很快(小于1秒)。在实体超过200万的情况下,需要大约20秒钟。每个节点维护一个碎片还是每个源维护一个碎片?

  3. 从Shard到master的合并尝试每15秒进行一次。这个参数应该调整吗?

我目前正在使用Lucene 3.1.0和JDK 1.6。两个64位核心的计算机具有8GB的RAM。当前JVM最大运行内存为4GB。

非常感谢您阅读这篇冗长的文章。如果您有任何提高性能的建议,我将不胜感激。我已经执行了Lucene通常规定的所有标准性能调整。


我不理解这里的分布式是什么意思。你说“所有节点共享一个NFS挂载点,索引驻留在那里。”所以所有部分都在同一物理系统上?很可能在这种情况下NFS会影响性能。 - Kees de Kooter
1个回答

2
这不是你寻找的答案,但是可以看一下Elastic Search。它是Lucene周围的分布式、集群服务层,可以通过HTTP查询或嵌入式运行。

而且它非常快,速度相当惊人。它似乎已经在底层适当地调整了Lucene,同时如果需要使用完整的Lucene配置选项,它还会暴露出来。

在分布式环境中让Lucene表现良好很难,就像你正在发现的那样,你最终会遇到令人讨厌的锁定问题。ElasticSearch旨在解决这个特定问题,因此您可以解决其他问题。


1
嘿,感谢您的快速回复。实际上,ES和SOLR都在考虑之中。但我现在正在与我继承的遗留代码作斗争。我希望我能够翻转开关,最终我会做到的。我不从事搜索优化业务,并将其留给该领域的专家。话虽如此,您是否对ES和SOLR进行过任何基准测试?或者您有一个推荐的选择吗? - Andy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接