【发布时间】:2012-07-06 10:55:28
【问题描述】:
所以我一直在研究从 Web 应用程序中实现 Lucene.Net 索引搜索和写入的最佳方法。我提出了以下要求:
- 需要允许并发搜索和访问索引(查询并行运行)
- 会有多个索引
- 不要求索引搜索完全是最新的(“实时”)
- 以某种频率运行作业以更新索引(每个索引的频率不同)
- 显然,希望以遵循 lucene“最佳实践”并且可以很好地执行和扩展的方式来完成所有这些工作
我在这里找到了一些有用的资源,以及一些关于 SO 的好问题,例如 this one
按照那篇文章作为指导,我决定尝试单例模式,其中包含一个用于管理索引的包装器的并发字典。
为了简单起见,我假设我只管理一个索引,在这种情况下,包装器可以成为单例。这最终看起来像这样:
public sealed class SingleIndexManager
{
private const string IndexDirectory = "C:\\IndexDirectory\\";
private const string IndexName = "test-index";
private static readonly Version _version = Version.LUCENE_29;
#region Singleton Behavior
private static volatile SingleIndexManager _instance;
private static object syncRoot = new Object();
public static SingleIndexManager Instance
{
get
{
if (_instance == null)
{
lock (syncRoot)
{
if (_instance == null)
_instance = new SingleIndexManager();
}
}
return _instance;
}
}
#endregion
private IndexWriter _writer;
private IndexSearcher _searcher;
private int _activeSearches = 0;
private int _activeWrites = 0;
private SingleIndexManager()
{
lock(syncRoot)
{
_writer = CreateWriter(); //hidden for sake of brevity
_searcher = new IndexSearcher(_writer.GetReader());
}
}
public List<Document> Search(Func<IndexSearcher,List<Document>> searchMethod)
{
lock(syncRoot)
{
if(_searcher != null && !_searcher.GetIndexReader().IsCurrent() && _activeSearches == 0)
{
_searcher.Close();
_searcher = null;
}
if(_searcher == null)
{
_searcher = new IndexSearcher((_writer ?? (_writer = CreateWriter())).GetReader());
}
}
List<Document> results;
Interlocked.Increment(ref _activeSearches);
try
{
results = searchMethod(_searcher);
}
finally
{
Interlocked.Decrement(ref _activeSearches);
}
return results;
}
public void Write(List<Document> docs)
{
lock(syncRoot)
{
if(_writer == null)
{
_writer = CreateWriter();
}
}
try
{
Interlocked.Increment(ref _activeWrites);
foreach (Document document in docs)
{
_writer.AddDocument(document, new StandardAnalyzer(_version));
}
}
finally
{
lock(syncRoot)
{
int writers = Interlocked.Decrement(ref _activeWrites);
if(writers == 0)
{
_writer.Close();
_writer = null;
}
}
}
}
}
理论上,这应该允许索引的线程安全单例实例(这里称为“index-test”),其中我有两个公开公开的方法,Search() 和 Write(),可以从内部调用ASP.NET Web 应用程序不关心线程安全? (如果这不正确,请告诉我)。
现在有一件事给我带来了一些麻烦:
我如何优雅地关闭 Global.asax.cs 文件中Application_End 上的这些实例,以便如果我想在 IIS 中重新启动我的 Web 应用程序,我不会遇到一堆 write.lock 失败等?
目前我能想到的只有:
public void Close()
{
lock(syncRoot)
{
_searcher.Close();
_searcher.Dispose();
_searcher = null;
_writer.Close();
_writer.Dispose();
_writer = null;
}
}
并在 Application_End 中调用它,但如果我有任何活跃的搜索者或作者,这会导致索引损坏吗?
非常感谢任何帮助或建议。谢谢。
【问题讨论】:
-
您的代码似乎找到了,但是由于您在构造函数中初始化了 Writer,所以我会简单地将其保持打开状态并删除 Write() 方法中的所有初始化/锁定。
-
最好将搜索器从目录中初始化,并仅在需要时打开编写器 - 如果我的阅读量远大于写作量?
-
我不知道,我没有太多经验,我通常在应用程序的生命周期内保持我的 IndexWriters 保持打开状态,并在我修改索引并使用 IndexWriter 打开搜索器时使用 commit()。 GetReader() 方法。
-
@JfBeaulac 所以也许我应该将其更改为始终打开编写器(调用关闭后除外)并创建一个 Commit() 方法?或者也许在每次写入后提交?感谢您的帮助。
-
@LelandRichardson 仅供参考,Lucene.net 是线程安全的,您不必使用任何同步机制(如 SingleIndexManagers、锁等)。只需创建/获取您的 IndexReaders/IndexWriters 并使用它们。我通常在应用程序范围内打开一个 IndexReader 和一个 IndexWriter 并在所有线程中使用它们。
标签: asp.net singleton lucene.net