在Lucene中,如何判断IndexSearcher或IndexWriter是否在另一个线程中使用?

Ira*_*chi 2 java lucene concurrency multithreading synchronization

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

所以,我试图在多线程设置中遵循这些指南.(一些线程编写,多个用户线程搜索).我不想在每次更改时重新打开索引,而是希望保持搜索器实例不超过一定的时间(比如20秒).

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

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

更糟糕的是,这是理论上可能发生的事情:可以同时执行多次搜索.(假设有数千名用户在同一索引上运行搜索).单个IndexSearcher实例可能永远不会被释放,因此可以关闭它.理想情况下,我想创建另一个IndexSearcher并将新请求指向它(虽然旧的仍然打开并运行之前已请求的搜索).当旧实例上运行的搜索完成后,我想关闭它.

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

有关于上述设计的任何建议/想法吗?

chu*_*ubs 9

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

这大致是我做的:

  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;
                  }
              }
          }
      }
  }
Run Code Online (Sandbox Code Playgroud)

所以我有一个单独的课程,我用于读者和作家.请注意,此类允许同时进行写入和读取,并且多个读者可以同时进行搜索.唯一的同步是快速检查,看你是否需要重新打开搜索者/作者.我没有在方法级别上进行同步,这种方法级别一次只允许一个读取器/写入器性能不佳.如果那里有活跃的搜索者,你就不能放弃搜索者.因此,如果你有很多读者进入它只是简单地搜索没有变化.一旦它变得苗条,下一个孤独的搜索者将重新打开肮脏的搜索者.对于流量暂停的低容量站点,这可能很有用.它仍然可能导致饥饿(即你总是阅读较旧和较旧的结果).你可以添加逻辑来简单地停止和重新初始化,因为它被注意到脏的时间早于X,否则我们现在懒得.这样你就可以保证搜索永远不会超过X.

作家可以用同样的方式处理.我倾向于记得定期关闭作者,以便读者注意到它的变化(提交它).我没有很好地描述这一点,但它的搜索方式大致相同.如果有活跃的作家,你就无法关闭作家.如果你是出门的最后一位作家关闭作家.你明白了.