C# 具有频繁更新索引的FieldCache
嗨C# 具有频繁更新索引的FieldCache,c#,java,.net,lucene,lucene.net,C#,Java,.net,Lucene,Lucene.net,嗨 我有lucene索引,它经常用新记录更新,我的索引中有5000000条记录,我正在使用FieldCache缓存我的一个数字字段。但在更新索引后,需要时间再次重新加载FieldCache(我重新加载缓存,因为文档中说DocID不可靠),因此如何通过只向FieldCache添加新添加的DocID来最小化此开销,因为此功能在我的应用程序中变成了瓶颈 IndexReader reader = IndexReader.Open(diskDir); int[] dateArr = FieldCache
我有lucene索引,它经常用新记录更新,我的索引中有5000000条记录,我正在使用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
我想要一种机制,通过只向数组中的索引添加新添加的文档来最小化这一时间。有一种技术是这样的
为了提高性能,但它仍然加载我们已经拥有的所有文档,我认为如果我们找到一种只向数组中添加新添加的文档的方法,就没有必要重新加载所有文档。您需要创建一个后台线程来构造
indexsearch
实例,每隔一段时间一个实例。继续使用当前的IndexSearcher
实例,直到后台线程中的新实例就绪。然后把新的换成当前的。从第一次打开索引时起,每个实例都充当索引的快照。请注意,FieldCache
的内存开销加倍,因为您需要同时在内存中使用两个实例。发生这种情况时,您可以安全地写入IndexWriter
如果您需要,您可以更进一步,使索引更改立即可用于搜索,尽管这可能会变得棘手。您需要将RAMDirectory
与上面的每个快照实例相关联,以将更改保留在内存中。然后创建第二个IndexWriter
,指向该RAMDirectory
。对于每次索引写入,您都需要同时写入IndexWriter
实例。对于搜索,您将在RAMDirectory
和磁盘上的普通索引中使用multisearch
。一旦与其耦合的索引搜索器
不再使用,则可以丢弃RAMDirectory
。我在这里对一些细节进行了润色,但这是总体思路
希望这有帮助。FieldCache使用弱引用来索引读卡器作为缓存的键。(通过调用未过时的
IndexReader.GetCacheKey
)使用FSDirectory
标准调用IndexReader.Open
将使用一个读卡器池,每个段一个
您应该始终将最里面的读取器传递给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];
var directory=FSDirectory.Open(新目录信息(“索引”);
var reader=IndexReader.Open(目录,只读:true);
var documentId=1337;
//抓取所有子阅读器。
var subReaders=新列表();
ReaderUtil.GatherSubReaders(子读取器,读取器);
//循环遍历所有子读取器。而子读取器ID高于
//子读取器中的最大文档id,转到下一步。
var subReaderId=documentId;
var subReader=subReaders.First(sub=>{
if(sub.MaxDoc()
假设您在磁盘上的FSDirectory中有1000条记录,并使用FieldCache加载它,并且您在RAMDirectory中有新的10条记录,正如您上面解释的那样,因此我们有两个ID为0的文档,…,10因为每个目录都有自己的docID,我无法创建具有唯一docID的集成FieldCache,在添加10次记录后,我还优化了索引。在这种情况下,docID可能会改变。第二部分的技巧是,您将在FSDirectory
和RAMDirectory
之间使用MultiSearcher
,这样FSDirectory
在RAMDirectory
开始更改之前就被打开了。因此,在它看来,给定ID的两个文档中只有一个存在。当您执行搜索时,MultiSearcher
处理合并这两个搜索。除非您在搜索之外使用FieldCache
?不过,我还是从第一部分开始,在后台打开第二个indexsearch
(或IndexReader
)实例,让它构建FieldCache
,然后将其替换掉。是的,我想在CustomScoreQueryTanks中的搜索之外使用FieldCache,但我想确保如果我将新文档添加到索引中,该文档的文档id在合并或优化时不会更改,因为如果它更改,则上述解决方案无法满足我的需要,因为我只想向FieldCache提供新添加的文档,以防止使用FieldCache再次加载所有文档,如果我能确保在合并/优化过程中哪个段读取器保持完整,那么我肯定可以根据您的解决方案加载它们,并重新加载其他段读取器值,这会逐渐提高性能,但仍然不理想,因为我希望合并/优化后读取器在技术上保持完整,它们也被淘汰,并被新创建的细分市场所取代。您能提供一些在当前设置中遇到问题的代码示例吗