Lucene NET throwing Fatal Error: AccessViolationException

Lucene NET throwing Fatal Error: AccessViolationException

我正在使用 Lucene NET v4.8 beta,我有一个方法每 5 秒在 SearcherManager 上调用 MaybeRefresh。 99.9% 的时间,一切正常。但是,在 0.1% 的情况下,我会收到致命的 AccessViolationException 错误。我不确定是什么导致了这个致命错误。这是完整的堆栈跟踪:

at System.IO.UnmanagedMemoryAccessor.ReadByte(Int64)
at Lucene.Net.Store.BufferedChecksumIndexInput.ReadByte()
at Lucene.Net.Store.DataInput.ReadInt32()
at Lucene.Net.Index.SegmentInfos+FindSegmentsFile.Run(Lucene.Net.Index.IndexCommit)
at Lucene.Net.Index.SegmentInfos.Read(Lucene.Net.Store.Directory)
at Lucene.Net.Index.StandardDirectoryReader.IsCurrent()
at Lucene.Net.Index.StandardDirectoryReader.DoOpenNoWriter(Lucene.Net.Index.IndexCommit)
at Lucene.Net.Index.DirectoryReader.OpenIfChanged(Lucene.Net.Index.DirectoryReader)
at Lucene.Net.Search.SearcherManager.RefreshIfNeeded(Lucene.Net.Search.IndexSearcher)
at Lucene.Net.Search.ReferenceManager`1[[System.__Canon, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].DoMaybeRefresh()
at Lucene.Net.Search.ReferenceManager`1[[System.__Canon, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MaybeRefresh()
...my method that calls MaybeRefresh...```

请注意:

我有 2 个独立的服务。一项服务通过 IndexWriter(服务 A)定期写入索引,另一项服务搜索索引并每 5 秒调用一次 MaybeRefresh(服务 B)。是服务 B 看到了这个致命错误。服务 A 工作正常,没有任何错误。所以我认为这与服务 B 有关,但为了完全透明而提及这一点,以防我遗漏了什么。

如果有人能深入了解这个由 Lucene 方法引起的致命错误,我们将不胜感激!

还请让我知道我应该添加的任何其他详细信息来描述此错误,如果有帮助的话。

首先,错误消息很可能表明您在同一组索引文件上多次打开 MMapDirectory,并且您遇到异常是因为两个实例都写入同一内​​存 space。不知道算不算BUG,不过需要注意的是写的时候不需要开RAM-intensiveMMapDirectory,直接用[=13就可以了=].

Directory dir = new SimpleFSDirectory(filePath);

话虽如此,以下建议将使上述观点变得毫无意义。

选项 1

通常情况下,您应该将打开单个索引的进程数限制为 1。如果您需要在读取发生的同时进行写入,则可以使用 near real-time search feature of Lucene.

执行此操作涉及的步骤是:

  1. 打开一个IndexWriter并保持打开状态(注册为单例)。
  2. 使用 IndexWriter 作为参数创建一个 SearcherManager(或者使用 writer.GetReader())。
  3. 使用SearcherManager进行搜索。
  4. 使用 IndexWriter 进行索引操作。
  5. Commit() 索引后。
  6. 添加文档后调用searcherManager.MaybeRefresh()

正如链接教程中所指出的,您可以使用 ControlledRealTimeReopenThread 在后台定期刷新 IndexReader

最后,要解决打开多个 Directory 实例的问题(这是最终导致此问题的原因),请使用单个进程进行写入和读取。由于写入通常比读取发生的频率低,我建议在搜索服务中执行所有这些操作,然后使用网络套接字(TCP、HTTP 等)从写入服务向搜索服务发送消息,以便写入 to/update/delete 来自索引。

选项 2

如果你想在多个进程中打开同一个索引,你可以使用Lucene.Net.Replicator模块用一个服务编写你的索引,然后发布它以复制到其他服务。 Lucene.Net.Replicator 通常推荐用于跨网络场中的多个节点复制相同的索引,但也可用于在一个服务中写入索引并在另一个服务中读取它。本质上,对于您的用例,您的每一项服务都有一个单独的索引目录。

但是,它还需要您构建一个网络服务来发布更新。主要区别是您不需要为 write/update/delete 索引设计专门的网站 API,而是可以使用现有的 API 在编写索引后发布索引。

参考文献:

  • Lucene real-time indexing?