Elasticsearch7.2中文教程翻译(九): 文档的读写原理
简介
Elasticsearch中的每个索引都被分成碎片,每个碎片可以有多个副本。这些副本称为复制组,在添加或删除文档时必须保持同步。如果我们做不到这一点,从一个副本中读取结果将与从另一个副本中读取结果非常不同。保持碎片副本同步并为从中读取的数据提供服务的过程称为数据复制模型。
Elasticsearch的数据复制模型基于主备份模型,在Microsoft Research的PacificA论文中得到了很好的描述。该模型基于从复制组中复制一个副本,该副本充当主碎片。其他副本称为复制碎片。主节点是所有索引操作的主要入口点。它负责验证它们并确保它们是正确的。一旦索引操作被主操作接受,主操作还负责将该操作复制到其他副本。
本节的目的是对Elasticsearch复制模型进行高层次的概述,并讨论它对写操作和读操作之间的各种交互的影响。
写模型的基础
Elasticsearch中的每个索引操作首先使用路由解析到一个复制组,通常基于文档ID。一旦确定了复制组,操作将在内部转发到组的当前主碎片。主碎片负责验证操作并将其转发到其他副本。由于副本可以脱机,所以不需要主副本复制到所有副本。相反,Elasticsearch维护一个应该接收操作的碎片副本列表。这个列表称为同步副本,由主节点维护。顾名思义,这些是一组“好”碎片副本,保证已经处理了所有已向用户确认的索引和删除操作。主变量负责维护这个不变式,因此必须将所有操作复制到这个集合中的每个副本。
主碎片遵循以下基本流程:
- 验证传入操作并在结构无效时拒绝它(例如:在需要一个数字的对象字段中)
- 在本地执行操作,即索引或删除相关文档。这还将验证字段的内容,并在需要时拒绝(例如:关键字值太长,无法在Lucene中进行索引)。
- 将操作转发到当前同步复制集中的每个副本。如果有多个副本,则并行执行。
- 一旦所有副本都成功地执行了操作并响应了主副本,主副本就会向客户端确认请求成功完成。
失败处理
索引过程中可能出现许多错误——磁盘可能损坏,节点之间可能断开连接,或者一些配置错误可能导致在副本上的操作失败,尽管在主副本上操作成功。这种情况并不常见,但主要的是必须回应它们。
在主节点本身失败的情况下,承载主节点的节点将向主节点发送关于它的消息。索引操作将等待(默认情况下最多1分钟),等待主进程将其中一个副本提升为新的主进程。然后,操作将被转发到新的主服务器进行处理。注意,主节点还监视节点的健康状况,并可能决定主动降级主节点。这通常发生在持有主节点的节点因网络问题与集群隔离时。详情请看这里。
一旦在主服务器上成功执行了操作,主服务器就必须处理在复制碎片上执行操作时可能出现的故障。这可能是由于副本上的实际故障或由于网络问题导致操作无法到达副本(或阻止副本响应)。所有这些都共享相同的最终结果:作为同步副本集一部分的副本将错过即将确认的操作。为了避免违反不变量,主服务器向主服务器发送一条消息,请求从同步复制集中删除有问题的碎片。只有在主服务器确认碎片已被删除后,主服务器才确认操作。注意,主节点还将指示另一个节点开始构建一个新的碎片副本,以便将系统恢复到正常状态。
在将操作转发到副本时,主服务器将使用副本来验证它仍然是活动的主服务器。如果主节点由于网络分区(或长时间GC)而被隔离,它可能会在意识到已降级之前继续处理传入索引操作。来自陈旧主节点的操作将被副本拒绝。当主服务器收到来自副本的响应,拒绝了它的请求(因为它不再是主服务器),那么主服务器就会向主服务器发出请求,并知道它已经被替换了。然后将操作路由到新的主服务器。
如果没有副本会发生什么?
这是一种有效的场景,可能是由于索引配置,也可能只是因为所有副本都失败了。在这种情况下,主要是没有任何外部验证的处理操作,这可能会有问题。另一方面,主服务器不能让其他碎片自己失败,而是请求主服务器代表它失败。这意味着主服务器知道主服务器是唯一一个好的副本。因此,我们保证主服务器不会将任何其他(过期的)碎片副本提升为新的主服务器,并且不会丢失任何索引到主服务器的操作。当然,由于此时我们只使用数据的单一副本运行,物理硬件问题可能会导致数据丢失。参见等待活动碎片以获得一些缓解选项。
读模式的基础
Elasticsearch中的读取可以是非常轻量级的ID查找,也可以是非常繁重的搜索请求,其中包含复杂的聚合,占用大量CPU资源。主备份模型的一个优点是,它保持所有碎片副本相同(飞行中操作除外)。因此,一个同步副本就足以满足读请求。
当节点接收到读请求时,该节点负责将其转发给包含相关切分的节点、整理响应并响应客户机。我们将该节点称为请求的协调节点。基本流程如下:
- 将读请求解析到相关碎片。注意,由于大多数搜索将被发送到一个或多个索引,因此它们通常需要从多个碎片中读取,每个碎片表示数据的不同子集。
- 从碎片复制组中选择每个相关碎片的活动副本。这可以是主要的,也可以是副本。默认情况下,Elasticsearch只是在碎片副本之间进行循环。
- 将碎片级别的读请求发送到所选副本。
- 结合结果并做出响应。注意,在get by ID查找的情况下,只有一个切分是相关的,可以跳过这一步。
碎片无响应
当碎片无法响应读请求时,协调节点将请求发送到相同复制组中的另一个碎片副本。重复失败可能导致没有可用的碎片副本。
为了确保快速响应,如果一个或多个碎片失败,以下api将以部分结果响应:
- Search
- Multi Search
- Bulk
- Multi Get
响应结果仍然返回一个200 OK HTTP状态码。碎片故障由响应头的timed_out和_shards字段表示。
一些简单的含义
这些基本流程中的每一个都决定了Elasticsearch作为读写系统的行为。此外,由于读写请求可以并发执行,所以这两个基本流彼此交互。这有几个内在的含义:
有效的读操作
在正常操作下,每个读操作为每个相关复制组执行一次。只有在出现故障的情况下,同一个切分的多个副本才会执行相同的搜索。
无效的读操作
由于主要的第一个索引是本地索引,然后复制请求,所以并发读取可能在确认更改之前就已经看到了更改。
默认两份数据
这个模型可以容错,同时只维护两个数据副本。这与基于quorum的系统形成了对比,后者容错的最小副本数为3。
失败
在失败的情况下,可能会出现以下情况:
单个碎片可能降低索引速度
由于主服务器在每次操作期间都要等待同步复制集中的所有副本,因此一个慢速碎片可能会减慢整个复制组的速度。这是我们为上面提到的读取效率所付出的代价。当然,一个单一的慢碎片也会减慢已经被路由到它的不幸的搜索。
脏读
一个独立的主节点可能返回不被承认的写操作。这是因为隔离的主服务器只有在向其副本发送请求或向主服务器发出请求时才会意识到它是隔离的。此时,操作已经被索引到主操作中,并且可以由并发读取。Elasticsearch每秒钟(默认情况下)ping一次主节点,如果不知道主节点,则拒绝索引操作,从而降低了这种风险。
冰山一角
本文档提供了Elasticsearch如何处理数据的高级概述。 当然,还有很多事情要发生在幕后。 主要术语,集群状态发布和主选举等都可以在保持系统正常运行方面发挥作用。 本文档也未涵盖已知和重要的错误(关闭和打开)。 我们认识到GitHub很难跟上。 为了帮助人们掌控这些,我们在我们的网站上维护了一个专用的弹性页面。 我们强烈建议阅读它。