2011-03-28 55 views
2


我的lucene索引经常用新记录进行更新,我在索引中有5,000,000条记录,并且正在使用FieldCache缓存我的一个数字字段。但在更新索引后,需要一段时间才能重新加载FieldCache(即时重新加载缓存原因文档,表示DocID不可靠),那么如何通过向FieldCache仅添加新添加的DocID来最大限度地减少此开销,从而导致此功能变为瓶颈应用。频繁更新索引的FieldCache


IndexReader reader = IndexReader.Open(diskDir); 
int[] dateArr = FieldCache_Fields.DEFAULT.GetInts(reader, "newsdate"); // This line takes 4 seconds to load the array 
dateArr = FieldCache_Fields.DEFAULT.GetInts(reader, "newsdate"); // this line takes 0 second as we expected 
// HERE we add some document to index and we need to reload the index to reflect changes 

reader = reader.Reopen(); 
dateArr = FieldCache_Fields.DEFAULT.GetInts(reader, "newsdate"); // This takes 4 second again to load the array 

我希望有一个机制,通过增加仅对新增文件到我们的数组中的索引减少这个时候有这样http://invertedindex.blogspot.com/2009/04/lucene-dociduid-mapping-and-payload.html 的技术来提高性能,但它仍然加载,我们已经把所有的文件和我认为如果我们找到一种方法只是将新添加的文档添加到阵列中,则无需重新加载它们全部

+0

你的代码的问题是我用内部/外部阅读器描述的。您将外部阅读器(DirectoryReader)传递给FieldCache。它认为这两个读者是不同的,并分别缓存它们。您需要使用最内层的阅读器,即段阅读器来为每个段填充它。这意味着它只会在你打电话给Reopen之后加载更改。我会在几分钟后为此发布一些代码。 – sisve 2011-04-03 06:50:35

回答

4

FieldCache使用弱引用来将索引读取器用作缓存的关键字。 (通过呼叫IndexReader.GetCacheKey已被废除)。对IndexReader.Open的标准呼叫FSDirectory将使用一个阅读器池,每个阅读器一个阅读器。

您应该始终将最内层的阅读器传递给FieldCache。查看ReaderUtil以获取某个帮助程序的内容以检索包含文档的个人阅读器。文档ID不会在段中更改,将其描述为不可预知/易失性时,它的含义是它将在两个索引提交之间更改。已删除的文件可能已被引用,段已合并,以及此类操作。

提交需要从磁盘中删除该段(合并/优化),这意味着新的阅读器不会有合并的段阅读器,并且只要所有较旧的阅读器关闭,垃圾收集器就会将其删除。

永远不要致电FieldCache.PurgeAllCaches()。它意味着测试,而不是生产使用。

添加2011-04-03;使用子阅读器的示例代码。

var directory = FSDirectory.Open(new DirectoryInfo("index")); 
var reader = IndexReader.Open(directory, readOnly: true); 
var documentId = 1337; 

// Grab all subreaders. 
var subReaders = new List<IndexReader>(); 
ReaderUtil.GatherSubReaders(subReaders, reader); 

// Loop through all subreaders. While subReaderId is higher than the 
// maximum document id in the subreader, go to next. 
var subReaderId = documentId; 
var subReader = subReaders.First(sub => { 
    if (sub.MaxDoc() < subReaderId) { 
     subReaderId -= sub.MaxDoc(); 
     return false; 
    } 

    return true; 
}); 

var values = FieldCache_Fields.DEFAULT.GetInts(subReader, "newsdate"); 
var value = values[subReaderId]; 
+0

谢谢西蒙,但我想确保如果我添加新文档到我的索引该文档的文档ID将永远不会改变合并或优化原因,如果它改变上述解决方案不符合我的需要,因为我想只向FieldCache提供新添加的文档,以防止再次使用FieldCache加载所有文档,如果我可以确保哪些分段阅读器在合并/优化期间保持完好,那么我可以根据您的解决方案和重新加载其他分段读取器的价值,它会逐步提高性能,但它仍然不是理想的,因为我想 – Ehsan 2011-04-01 05:56:14

+0

尽管技术上合并/优化后读取器完好无损,但它们也被废弃并用新创建的分段替换。你能否提供一些在当前设置中遇到问题的代码示例? – sisve 2011-04-01 13:47:06

+0

谢谢你的代码是完美的西蒙 – Ehsan 2011-04-03 19:25:36

1

以下是我解决此问题的一种方法。您需要创建一个后台线程来构造IndexSearcher实例,每隔一段时间一次。继续使用当前的IndexSearcher实例,直到后台线程中的新线程准备就绪。然后换出新的是你现在的那个。每个实例都充当索引从第一次打开时的快照。请注意,FieldCache的内存开销增加了一倍,因为您一次需要在内存中创建两个实例。在发生这种情况时,您可以放心地写信至IndexWriter

如果您需要,您可以通过立即进行索引更改以进行搜索来进一步探索,尽管它可能会变得棘手。您需要将RAMDirectory与上面的每个快照实例相关联,以将更改保留在内存中。然后创建第二个IndexWriter指向那个RAMDirectory。对于每个索引编写,您需要写入两个IndexWriter实例。对于搜索,您将在RAMDirectory之间使用MultiSearcher,并在磁盘上使用正常索引。一旦IndexSearcher不再使用,RAMDirectory可以扔掉。我在这里详述了一些细节,但是这是一般的想法。

希望这会有所帮助。

+0

假设你在磁盘上的FSDirectory中有1000条记录并使用FieldCache加载它,并且你在RAMDirectory中有新的10条记录,就像你上面解释的那样,所以我们有两个ID为0,...,10的文档,因为每个目录都有它自己的docID我不能创建一个具有唯一docID的集成FieldCache,并且在添加记录10次之后,我还优化了我的索引。在这种情况下,docID可能会改变。 – Ehsan 2011-03-28 09:08:32

+0

第二部分的技巧是,你将在'FSDirectory'和'RAMDirectory'之间使用'MultiSearcher',这样''''''''''''''''在'RAMDirectory'开始变化之前打开'FSDirectory'。所以它只是看起来存在给定ID的两个文件中的一个。当您执行搜索时,'MultiSearcher'处理合并这两者。除非你在搜索之外使用FieldCache?不过,我会从第一部分开始,在后台打开第二个'IndexSearcher'(或'IndexReader')实例,让它构建FieldCache,然后将其交换出去。 – WhiteFang34 2011-03-28 09:20:15

+0

是的,我想在CustomScoreQuery搜索外使用FieldCache – Ehsan 2011-03-28 11:53:25