java - 在 Lucene 中,如何确定 IndexSearcher 或 IndexWriter 是否正在另一个线程中使用?

标签 java multithreading concurrency lucene synchronization

Lucene 文档指出 IndexSearcher 和 IndexWriter 的单个实例应该用于整个应用程序中的每个索引,并跨所有线程。此外,在重新打开索引之前,对索引的写入将不可见。

因此,我尝试在多线程设置中遵循这些指南。 (几个线程写作,多个用户线程搜索)。我不想在每次更改时都重新打开索引,相反,我想让搜索器实例不超过一定时间(比如 20 秒)。

中央组件负责打开索引读取器和写入器,并保持单个实例并同步线程。我跟踪任何用户线程最后一次访问 IndexSearcher 的时间,以及它变脏的时间。如果更改后 20 秒后有人需要访问它,我想关闭搜索器并重新打开它。

问题是我不确定之前对搜索器的请求(由其他线程发出)是否已经完成,以便我可以关闭 IndexSearcher。这意味着如果我关闭并重新打开在所有线程之间共享的单个 IndexSearcher 实例,则可能会在其他线程中同时进行搜索。

更糟糕的是,理论上可能会发生以下情况:始终可以同时执行多项搜索。 (假设您有成千上万的用户在同一个索引上运行搜索)。单个 IndexSearcher 实例可能永远不会空闲,因此可以将其关闭。理想情况下,我想创建另一个 IndexSearcher 并将新请求定向到它(而旧的仍然打开并运行之前已经请求的搜索)。在旧实例上运行的搜索完成后,我想关闭它。

同步 IndexSearcher(或 IndexWriter)的多个用户以调用 close() 方法的最佳方法是什么? Lucene 是否为此提供任何功能/设施,或者应该完全由用户代码完成(例如使用搜索器计算线程数,并在每次使用时增加/减少计数)?

对于上述设计有什么建议/想法吗?

最佳答案

值得庆幸的是,在最近的版本(3.x 或 2.x 后期)中,他们添加了一个方法来告诉您在打开搜索器后是否有任何文字。 IndexReader.isCurrent() 将告诉您自此阅读器打开后是否发生了任何更改。因此,您可能会创建一个简单的包装器类来封装读取和写入,并且通过一些简单的同步,您可以提供一个类来管理所有线程之间的所有这些。

我大致是这样的:

  public class ArchiveIndex {
      private IndexSearcher search;
      private AtomicInteger activeSearches = new AtomicInteger(0);
      private IndexWriter writer;
      private AtomicInteger activeWrites = new AtomicInteger(0);

      public List<Document> search( ... ) {
          synchronized( this ) {
              if( search != null && !search.getIndexReader().isCurrent() && activeSearches.get() == 0 ) {
                 searcher.close();
                 searcher = null;
              }

              if( search == null ) {
                  searcher = new IndexSearcher(...);
              }
          }

          activeSearches.increment();
          try {
              // do you searching
          } finally {
              activeSearches.decrement();
          }
          // do you searching
      }


      public void addDocuments( List<Document> docs ) {
          synchronized( this ) {
             if( writer == null ) {
                 writer = new IndexWriter(...);
             }
          }
          try {
              activeWrites.incrementAndGet();
              // do you writes here.
          } finally {
              synchronized( this ) {
                  int writers = activeWrites.decrementAndGet();
                  if( writers == 0 ) {
                      writer.close();
                      writer = null;
                  }
              }
          }
      }
  }

所以我有一个单独的类供读者和作者使用。注意这个类允许同时写和读,多个读者可以同时搜索。唯一的同步是快速检查以查看是否需要重新打开搜索器/编写器。我没有在方法级别上同步,这一次只允许一个读取器/写入器,这在性能方面很糟糕。如果那里有活跃的搜索者,你就不能放弃搜索者。所以如果你有很多读者进来,它只是简单地搜索而不做任何改变。一旦它精简,下一个单独的搜索器将重新打开脏搜索器。这对于流量会暂停的低流量站点可能非常有用。它仍然可能导致饥饿(即你总是在阅读越来越旧的结果)。你可以添加逻辑来简单地停止并重新初始化,如果它被注意到脏的时间比 X 早,否则我们像现在一样懒惰。这样你就可以保证搜索永远不会早于 X。

作家可以用同样的方式处理。我倾向于记得定期关闭编写器,以便读者会注意到它的更改(提交)。我没有很好地描述它,但这与搜索方式大致相同。如果那里有活跃的作家,你就不能关闭作家。如果你是最后一个出门的作家,请关闭作家。你明白了。

关于java - 在 Lucene 中,如何确定 IndexSearcher 或 IndexWriter 是否正在另一个线程中使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8195166/

相关文章:

Java服务器将html页面从文件发送到浏览器

node.js - NodeJS 服务器使用多线程吗?

multithreading - 为什么 UI 框架必须/应该是单线程的?

客户端-服务器应用程序上的 Linux 套接字输入通知

java - 如何暂停一个线程并在它停止的地方恢复它

JavaFx:组合框表格单元格双击

java - 无法在 JDBC 中执行 MySQL 存储过程,出现空指针异常

multithreading - 使用多线程处理硬盘驱动器上的文件有用吗?

c# - 异步 LINQ - 不懒惰?多线程?

Java 更改卷 OSX