Ira*_*chi 2 java lucene concurrency multithreading synchronization
Lucene文档指出,IndexSearcher和IndexWriter的单个实例应该用于整个应用程序中的每个索引,以及所有线程.此外,在重新打开索引之前,对索引的写入将不可见.
所以,我试图在多线程设置中遵循这些指南.(一些线程编写,多个用户线程搜索).我不想在每次更改时重新打开索引,而是希望保持搜索器实例不超过一定的时间(比如20秒).
中央组件负责打开索引读取器和编写器,并保留单个实例并同步线程.我跟踪上次任何用户线程访问IndexSearcher的时间,以及它变脏的时间.如果有人需要在更改过去20秒后访问它,我想关闭搜索器并重新打开它.
问题是我不确定先前对搜索者(由其他线程制作)的请求是否已经完成,因此我可以关闭IndexSearcher.这意味着如果我关闭并重新打开在所有线程之间共享的单个IndexSearcher实例,则可能会在其他某个线程中同时进行搜索.
更糟糕的是,这是理论上可能发生的事情:可以同时执行多次搜索.(假设有数千名用户在同一索引上运行搜索).单个IndexSearcher实例可能永远不会被释放,因此可以关闭它.理想情况下,我想创建另一个IndexSearcher并将新请求指向它(虽然旧的仍然打开并运行之前已请求的搜索).当旧实例上运行的搜索完成后,我想关闭它.
同步IndexSearcher(或IndexWriter)的多个用户以调用close()方法的最佳方法是什么?Lucene是否为此提供任何功能/设施,或者它应该完全由用户代码完成(比如使用搜索器计算线程,并在每次使用时增加/减少计数)?
有关于上述设计的任何建议/想法吗?
值得庆幸的是,在最近的版本(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.
作家可以用同样的方式处理.我倾向于记得定期关闭作者,以便读者注意到它的变化(提交它).我没有很好地描述这一点,但它的搜索方式大致相同.如果有活跃的作家,你就无法关闭作家.如果你是出门的最后一位作家关闭作家.你明白了.