倒排索引分片 lucene_Lucene的近实时分段索引复制

倒排索引分片 lucene

[ TL; DRApache Lucene 6.0悄然引入了一项强大的新功能,称为近实时(NRT)段复制 ,可将索引从一台服务器高效可靠地复制到另一台服务器,并利用越来越快和便宜的局域网技术。 两种流行的搜索服务器( ElasticsearchSolr )都尚未使用它,但是它应该为它们带来索引和搜索性能以及鲁棒性的大幅提高。]

Lucene具有独特的一次写入分段式体系结构 :最近索引的文档以仅追加,一次写入的方式写入新的自包含分段 :一旦写入,这些分段文件将永远不会改变。 当使用过多的RAM来保存最近索引的文档时,或者当您要求Lucene刷新搜索器以便可以搜索所有最近索引的文档时,就会发生这种情况。

随着时间的流逝,较小的段将合并为较大的段 ,并且索引随时都有活动段文件的对数“阶梯”结构。 与连续不断地就地更新文件的数据库相比,这是一个不寻常的设计,并且在Lucene中会冒充各种出色的高级功能。 例如:

  • 高效的ACID交易
  • 索引的时间点视图即使在并发索引下也不会改变,从而实现了稳定的用户交互,例如分页和下钻。
  • 即使在并发索引下,也可以在索引中无限期保留多个时间点快照(提交点),这对于获取Lucene索引的热备份很有用。

ZFS文件系统具有类似的功能,例如高效的整个文件系统快照,之所以可以这样做是因为它在文件块级别还使用了一次写入设计:在您喜欢的文本编辑器中更改文件并保存时,ZFS分配新块并将新版本写入这些块,而原始块保持不变。

这种一次写入的设计还使Lucene 在编写索引文件时可以使用优化的数据结构并应用强大的压缩技术 ,因为Lucene知道此段的值以后不会更改。 例如,您不必告诉Lucene您的doc值字段仅需要1个字节的存储空间,也不需要稀疏值等等。:Lucene通过查看将要写入每个新值的所有值来自己弄清楚这一点。分割。


最后,本文的主题:这种设计使Lucene可以有效地将搜索索引从一台服务器(“主服务器”)复制到另一台服务器(“副本服务器”):为了将最近的索引更改从主服务器同步到副本服务器,您只需要查看索引目录中的文件名,而不是其内容。 任何新文件都必须被复制,以前复制的任何文件都不需要再次复制,因为它们永远不会改变!

利用这一点,我们很久以前就在Lucene添加了一个复制器模块,正是使用了这种方法,并且效果很好。 但是,要使用这些API复制最近的索引更改,每次您想要同步时,必须首先在主索引上提交更改。 不幸的是,这是一项昂贵的操作,在多个新近写入的文件上调用fsync ,并且与打开新的NRT搜索器的本地索引相比,大大增加了同步延迟。

文档或段复制?

提交以复制Lucene最近的索引更改的要求如此讨厌的限制是,当流行的分布式搜索服务器ElasticsearchSolr添加了分布式索引支持时,他们选择根本不使用Lucene的复制模块,而是创建自己的文档复制 ,其中主副本和所有副本都冗余索引并合并所有传入文档。

尽管这似乎是使副本保持同步的自然方法,但存在以下缺点:

  • 整个集群上CPU / IO资源的使用成本更高 :所有节点必须执行相同的冗余索引和合并工作,而不仅仅是一个主节点。 当大型合并正在运行并且干扰并发搜索时,这尤其痛苦,并且在需要多个副本以支持高查询率的群集上的成本更高。 仅此一项,就可以使Elasticsearch和Solr在集群范围的索引编制和搜索吞吐量方面大大提高。
  • 不一致的风险 :确保在主索引和所有副本中精确索引相同的文档集是棘手的,并且会导致Elasticsearch过去在网络行为异常时丢失文档的问题 。 例如,如果其中一个副本在索引文档时抛出异常,或者发生网络故障,则该副本现在会丢失其他副本和主副本所包含的文档。
  • 停机后恢复节点的成本高昂 :当副本停机一段时间后又重新启动时,它必须重播(重新索引)停机时到达的所有新索引文档。 这很容易成为一个非常大的数字,需要大量的事务日志并且需要很长时间才能赶上,或者必须回退到昂贵的完整索引副本。 相反,基于段的复制仅需要复制新的索引文件。
  • 高代码复杂性 :处理副本可能Swift失去同步的众多可能情况的代码变得非常复杂,尤其是处理主交换机(因为旧的主交换机崩溃)。
  • 没有“时间点”一致性 :主数据库和副本数据库按其自己的时间表刷新,因此它们通常都在搜索索引的略有不同且无可比拟的视图。


最后,在Lucene 6.0中, 由于其他重要功能(如尺寸点)的影响 ,我们悄悄改进了Lucene的复制模块,支持NRT段复制 ,可以在刷新后复制新段文件,而无需首先调用commit。 当与不断发展的趋势相结合时,这种趋势将变得尤为引人注目,而这种趋势将朝着更快,更便宜的局域网技术发展。

它是如何工作的?

尽管逻辑设计简单明了(“仅将新的段文件从主文件复制到副本”),但实现起来颇具挑战性,因为这增加了另一个并发操作(副本通过网络缓慢复制索引文件),以及许多其他操作。已经在IndexWriter发生过,例如打开新的阅读器,建立索引,删除,段合并和提交。 幸运的是,我们能够建立在现有的Lucene功能(如NRT读取器)的基础上,以编写新的段并识别其所有文件和快照,以确保在副本完成复制之前不会删除段文件。

两个主要的API是PrimaryNode ,它包含包含本地IndexWriter实例的主节点的所有状态,以及副本的ReplicaNode 。 副本就像索引编写器一样,因为它们还会创建和删除索引文件,因此它们在启动时会获得Lucene的索引写锁定,以检测无意间的滥用。 实例化每个副本时,将提供一个指向其对应主数据库运行位置的指针,例如主机或IP地址和端口。

主节点和副本节点都公开SearcherManager因此您可以随时获取和发布最新的搜索器。 在主节点上搜索也可以,并且可以匹配当今所有Elasticsearch和Solr节点的行为(因为它们始终同时进行索引和搜索),但是您也可以选择将主节点专用于仅建立索引。

您可以从主节点使用IndexWriter像往常一样进行索引更改,然后当您要搜索最近索引的文档时,您要求主节点刷新。 在后台,Lucene将从其loca IndexWriter打开一个新的NRT阅读器,收集其引用的索引文件,并通知所有连接的副本。 然后,副本计算它们丢失的索引文件,然后从主副本复制它们。

通常将文档删除作为位集直接从IndexWriter到NRT IndexReader承载在内存中,而是直接写入文件系统并进行复制。 如果所有副本都成功,则首先将所有文件复制到临时文件,然后最后(原子上)重命名。 Lucene现有的端到端校验和用于验证在不稳定的网络链接,错误的RAM或CPU传输过程中没有翻转任何位。 最后,内存中的SegmentInfos段文件( SegmentInfos实例)在网络上进行序列化并发送到副本,副本再进行反序列化并通过本地SearcherManager打开NRT搜索SearcherManager 。 保证副本上的结果搜索器搜索与主视图完全相同的时间点视图。

所有这些都与副本和可选主节点上的正在进行的搜索同时进行。 这些搜索将看到旧的搜索器,直到复制完成并打开新的搜索器。 您还可以使用Lucene的现有SearcherLifetimeManager ,如果需要将较早的搜索者保留一段时间,则可以使用其长版本跟踪每个时间点搜索者。

副本节点和主节点都公开了独立的提交 API。 您可以根据您的耐用性要求选择调用它们,甚至可以错开跨节点的提交以减少对群集范围的搜索容量的影响。

没有交易记录

请注意,Lucene不提供事务日志! 更一般而言,由NRT复制链接的主要+副本集群的行为非常类似于单个JVM上的单个IndexWriter和NRT阅读器。 这意味着您有责任准备从最后一个提交点开始重放文档以重新建立索引,如果整个群集崩溃并再次启动,或者如果主节点崩溃,则副本无法复制新的时间点刷新。

请注意,在文件系统级别,主节点和副本节点的Lucene索引之间没有区别,这使得关闭旧的主副本并将其中一个副本提升为新的主副本变得很容易。

合并中

使用NRT复制,主节点还可以进行所有段合并。 这很重要,因为合并是CPU和IO的繁重操作,并且会干扰正在进行的搜索。

一旦主数据库完成了合并,并且在将合并的段安装到其索引中之前,它将使用Lucene的合并的段预热API为所有副本提供预复制合并的段的机会。 这意味着合并绝不应阻止刷新,因此即使大型合并的段仍在复制中,Lucene也会保持快速刷新。 一旦所有正在运行的副本都预复制了合并,则主副本将安装合并的段,并且在下一次刷新之后,所有副本也将进行复制。

我们已经讨论过让副本执行自己的合并,但是我怀疑这将是一个不好的权衡。 局域网(例如10 GB以太网)正在Swift变得越来越便宜,并且要求副本也进行合并将必然影响搜索性能。 试图确保副本同时执行与主副本完全相同的合并,并且否则会破坏副本之间的相同时间点视图,这也将非常复杂。

抽象

主节点和副本节点是抽象的:您必须自己实现某些功能。 例如,如何将字节从主数据库复制到副本的底层机制取决于您。 Lucene不提供此功能,除非在其单元测试中使用简单的点对点TCP“每个连接线程”服务器。 您可以选择使用rsync,robocopy, netty服务器,中央文件服务器,运营商信鸽,UDP多播(如果同一子网上有许多副本,可能会很有用)等。

Lucene还没有提供任何分布式的领导者选举算法来在当前主节点崩溃时选择新的主节点,也没有提供启发式方法来检测已降级的主节点或副本。 但是,一旦您选择了新的主数据库,Lucene将负责所有副本的切换,从旧的主数据库中删除陈旧的部分复制的文件,等等。

最后,Lucene不会提供任何负载平衡来将查询定向到负载最小的副本,也不会提供任何集群状态来跟踪哪个节点是主节点以及哪些是副本。
Apache Zookeeper对于此类共享的分布式状态很有用。 这些零件全由您决定!

预期的故障模式

使用NRT索引复制进行服务器索引和搜索的集群可能会出错,并且我们编写了一个邪恶的随机压力测试用例,以在这种情况行使Lucene的处理能力。 该测试通过为主节点和多个副本节点生成JVM子进程来创建集群,并开始建立索引和复制,同时将异常缓慢的网络(例如随机翻转位的网络,随机JVM崩溃(SIGSEGV!))随机应用到特定节点上。主节点或副本节点,然后进行恢复等。此测试用例揭示了各种有趣的极端情况!

一个特别重要的情况是主节点发生故障(崩溃,断电或被故意杀死)。 在这种情况下,一个副本(最好是由分布式选举决定从旧主副本复制文件中最远的副本)被提升为新的主节点。 然后,所有其他副本都切换到新的主数据库,并且在此过程中,必须删除从旧的主数据库复制或部分复制但未被新的主数据库引用的所有文件。 索引到主文档但未复制到该副本的所有文档都需要再次索引,这是调用者的责任(无事务日志)。

如果整个集群崩溃或断电,则在重新启动时,您需要确定哪个索引(主索引或副本索引)具有“最新”提交点,并启动该主机上的主节点和其他主机上的副本节点。 这些副本节点可能需要删除一些索引文件才能切换到新的主节点,Lucene会负责。 最后,您将必须重播上一次成功提交之后到达的所有文档。

其他有趣的情况包括:副本仍在预复制合并时崩溃; 由于出现新刷新而仍在复制先前刷新中的文件的副本,因此处于落后状态; 主节点也已更改后,副本将关闭并稍后又恢复。

缺点

分段复制也有一些小缺点:

  • 全新代码 :此功能是一个相当新的功能,也非常复杂,尚未得到广泛使用。 可能有令人兴奋的错误! 欢迎补丁!
  • 刷新时间稍慢 :在主数据库上刷新后,包括将文档删除也写入磁盘,然后我们必须将所有新文件复制到副本并在副本上打开新的搜索器,从而增加了一些时间才能看到文档以进行搜索与IndexWriter的直接NRT阅读器进行比较时,该副本上的内容会有所变化。 如果确实存在问题,则可以使用主节点进行搜索,并且刷新延迟非常接近普通NRT阅读器所提供的延迟。
  • 索引问题可能会被复制 :如果出现问题,并且主数据库以某种方式写入损坏的索引文件,则该损坏的文件也将被复制到所有副本。 但这几天很少见,尤其是在Lucene 端到端校验和的情况下

总结

NRT段复制为流行的分布式搜索服务器提供了可观的性能和可靠性改进的机会,尤其是当与不断发展的趋势以更快,更便宜的局域网相结合时。 不幸的是,对于Elasticsearch和Solr而言,此功能来不及了,但我希望下一代流行的分布式搜索服务器及其用户可以从中受益!

翻译自: https://www.javacodegeeks.com/2017/09/lucenes-near-real-time-segment-index-replication.html

倒排索引分片 lucene

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值