FieldCache经常更新索引

时间:2011-03-28 06:49:13

标签: c# java .net lucene lucene.net

您好
我有经常用新记录更新的lucene索引,我的索引中有5,000,000条记录,我正在使用FieldCache缓存我的一个数字字段。但在更新索引之后需要时间再次重新加载FieldCache(重新加载缓存导致文档说DocID不可靠)所以如何通过仅向FieldCache添加新添加的DocID来最小化这种开销,导致此功能转为我的瓶颈应用


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 提高性能,但它仍然加载我们已经拥有的所有文档,我认为如果我们找到一种方法只添加新添加的文档到数组,则无需重新加载它们

2 个答案:

答案 0 :(得分:4)

FieldCache使用对索引读取器的弱引用作为其缓存的键。 (通过调用尚未过时的IndexReader.GetCacheKey。)使用IndexReader.OpenFSDirectory的标准调用将使用一个读者池,每个段一个。

您应该始终将最里面的阅读器传递给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];

答案 1 :(得分:1)

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

如果您需要,可以通过立即使用索引更改进行搜索来更进一步,尽管它可能会变得棘手。您需要将RAMDirectory与上面的每个快照实例相关联,以保持内存中的更改。然后创建指向IndexWriter的第二个RAMDirectory。对于每个索引写入,您需要写入IndexWriter个实例。对于搜索,您将使用MultiSearcher上的RAMDirectory和磁盘上的正常索引。一旦RAMDirectory不再使用IndexSearcher,{{1}}就会被丢弃。我在这里略过一些细节,但这是一般的想法。

希望这有帮助。