【问题标题】:How to improve Lucene performance in a distributed environment?如何在分布式环境中提高 Lucene 性能?
【发布时间】:2011-09-08 09:42:14
【问题描述】:

在分布式环境中搜索主分片实现时,我面临着很长的搜索时间(大约 10 秒)。然而,通过 Luke 的相同查询会在毫秒内返回。

该应用程序是一个分布式系统。所有节点共享索引所在的公共 NFS 挂载。为简单起见,让我们考虑两个节点Node1Node2/etc/fstab 条目如下。

nfs:/vol/indexes /opt/indexes nfs rw,suid,nodev,rsize=32768,wsize=32768,soft,intr,tcp 0 0

有多个提要(例如 Feed1Feed2 )命中系统,每个节点的每个提要都有一个分片,每个提要都有一个主分片。索引看起来像

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. 在实体数量少于一百万(亚秒级响应)的提要上延迟较低。实体超过 200 万的提要大约需要 20 秒。我应该每个节点只维护 1 个 Shard,而不是每个提要每个节点 1 个 Shard?

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

我目前使用的是 Lucene 3.1.0 和 JDK 1.6。这些盒子是两个具有 8 GB RAM 的 64 位内核。目前,JVM 以最大 4 GB 运行。

非常感谢任何提高性能的建议。我已经执行了 Lucene 通常规定的所有标准性能调整。非常感谢您阅读这篇冗长的帖子。

【问题讨论】:

  • 我不明白这里分发的内容。您说“所有节点共享索引所在的公共 NFS 挂载”。那么所有部分都在同一个物理系统上吗?在这种情况下,NFS 很可能会损害性能。

标签: performance lucene


【解决方案1】:

这可能不是您要寻找的答案,但请查看Elastic Search。它是一个围绕 Lucene 的分布式集群服务层,可以通过 HTTP 进行查询,也可以嵌入运行。

而且速度很快,非常可笑。它似乎在幕后对 Lucene 进行了适当的调整,同时如果您需要使用它们,仍然会公开完整的 Lucene 配置选项。

让 Lucene 在分布式环境中运行是很困难的,正如您所发现的,最终会遇到令人讨厌的锁定问题。 ElasticSearch 旨在解决该特定问题,因此您可以解决其他问题。

【讨论】:

  • 您好,感谢您的快速回复。事实上 ES 和 SOLR 都在桌面上。但我在这里战斗的是我继承的遗留代码。我希望我能拨动开关,我最终会的。我不从事搜索优化业务,并将其留给该领域的专家。话虽如此,您是否对 ES over SOLR 进行了任何基准测试?和/或您对其中一个有什么建议。
猜你喜欢
  • 1970-01-01
  • 2013-09-26
  • 1970-01-01
  • 2016-11-01
  • 2021-12-10
  • 2018-11-15
  • 2011-07-01
  • 2013-02-06
  • 2021-10-14
相关资源
最近更新 更多