Lucene可以同时删除和更新!

很久以前,Lucene只能使用单个线程将新段写入磁盘。 文档的实际索引是将传入文档转换为内存中段数据结构的昂贵过程,可以使用多个线程运行,但那时,将这些内存中索引写入Lucene段的过程是单线程的。

我们修复了这一问题, 现在已经有6年多了在并发硬件上可以产生很大的索引吞吐量

如今,硬件变得更加并发了,我们终于在处理已删除的文档和更新doc值方面做了同样的事情!

在Lucene的下一个主要版本(7.0)之前 ,此更改适时显示了在使用高度并发硬件的专用测试语料库上,更新整个文档时索引吞吐量提高了53%,更新文档值时索引速度提高了7.4X – 8.6X。 i3.16xlarge EC2实例 )。

缓冲与应用

当您要求Lucene的IndexWriter删除文档或更新文档(先删除原子然后添加),或更新文档的doc-values字段时,通常将其传递给Term ,例如针对主键字段id ,标识要更新的文档。 但是IndexWriter不会立即执行删除。 相反,它会缓冲所有此类删除和更新,并且只有在它们使用过多RAM或刷新近乎实时的Reader或调用commit或开始合并时才最终批量应用它们。

将这些术语解析为实际的Lucene文档ID的过程非常昂贵,因为Lucene必须访问所有段并为每个术语执行主键查找。 批量执行查找会提高效率,因为我们以unicode顺序对术语进行了排序,因此我们可以对每个段的术语词典和发布进行一次顺序扫描。

随着时间的流逝,我们还优化了主键查找以及删除和更新的缓冲,出现了诸如LUCENE-6161LUCENE-2897LUCENE-2680LUCENE-3342之类的问题 。 如果我们的快速BlockTree术语词典可以从有限状态换能器术语索引中得知所请求的术语不可能存在于该段中,则有时可以为每个段节省磁盘搜索。

尽管如此,就像我们编写此代码一样快,一次只允许一个线程运行它,而对于处理大量更新的工作负载,一个线程可能成为主要瓶颈。 过去,我们已经看到用户对此进行询问,因为在解决删除问题时,由于没有其他事情发生,因此看起来好像挂起了IndexWriter 。 索引缓冲区越大,挂起时间就越长。

当然,如果您只是将新文档添加到Lucene索引中,而从未更新过以前已索引的文档,这是当今使用Lucene广泛用于日志分析的常见用例,那么这对您来说都不重要!

并发很难

进行此更改后, IndexWriter仍将删除和更新缓存到数据包中 ,但是之前,当每个数据包也被缓存以供以后的单线程应用程序使用时, IndexWriter现在立即使用当前索引将该数据包中的删除和更新解析为受影响的文档。线。 因此,您可以获得与通过IndexWriter发送的索引线程一样多的并发性。

由于IndexWriter的并发非常复杂,因此更改非常困难,我现在坚信我们需要通过某种方式重构IndexWriter来解决技术性债务。 此类很难实施,因为它必须处理许多复杂而昂贵的并发操作:正在进行的索引编制,删除和更新; 刷新新读者; 编写新的段文件; 提交对磁盘的更改; 合并细分并添加索引。 有许多锁,不仅包括IndexWriter的监视器锁,而且还有许多其他内部类,这些锁今天很容易意外触发死锁。 欢迎补丁!

由于我们进行了7.0的广泛随机测试,原始更改还导致了一些神秘的 测试 失败

不幸的是,这种复杂的并发性阻止了我完成删除和更新的最后步骤,使其完全一致:编写新的段文件。 此文件写入将使用内存中解析的文档ID,并为已删除的文档写入一个新的按段位集,或为每个字段写入一个新的文档值列以更新文档值。

这通常是一种快速的操作,除了大型索引之外,整个文档值更新的整个列都可以调整大小。 但是由于必须对影响文档的每个段都执行此操作,因此单线程处理绝对仍然是一个小瓶颈,因此,一旦我们成功简化了IndexWriter的并发性,并且也使我们的文件写入并发,那就太好了。

翻译自: https://www.javacodegeeks.com/2017/07/lucene-gets-concurrent-deletes-updates.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值