3

读和写文档

本节简要介绍Elasticsearch的数据复制模型。

介绍

Elasticsearch中的每个索引都分为碎片,每个碎片可以有多个副本,这些副本称为复制组,在添加或删除文档时必须保持同步。如果我们不这样做,从一个副本中读取将导致与从另一个副本读取的结果截然不同,保持碎片副本同步并从中提供读取的过程就是我们所说的数据复制模型。

Elasticsearch的数据复制模型基于主备模型,并在微软研究院的PacificA paper论文中得到了很好的描述。该模型基于从复制组中持有一个副本作为主碎片,其他副本称为副本碎片。主碎片服务作为所有索引操作的主要入口点,它负责验证它们并确保它们是正确的,一旦主碎片接受了索引操作,主碎片负责将操作复制到其他副本。

本节的目的是对Elasticsearch复制模型作一个高层次的概述,并讨论它对读和写操作之间的各种交互的影响。

基础写模型

Elasticsearch中的每个索引操作首先使用路由解析为一个复制组,通常基于文档ID,一旦确定了复制组,操作将在内部转发到当前组的主碎片,主碎片负责验证操作并将其转发到其他副本。由于副本可以脱机,主碎片不需要复制到所有副本,相反,Elasticsearch维护一个应该收到操作的碎片副本列表,这个列表称为同步副本,由主节点维护。顾名思义,这些是一组“好的”碎片副本,它们保证已经处理了已向用户确认的所有索引和删除操作,主碎片负责维护这个不变量,因此必须将所有操作复制到这个集合中的每个副本。

主碎片遵循这个基本流程:

  1. 验证传入的操作并在结构上无效时拒绝它(例如:有一个对象字段,其中一个数字是预期的)。
  2. 在本地执行操作,即索引或删除相关文档,这还将验证字段的内容并在必要时拒绝(例如:在Lucene中用于索引的一个关键字太长)。
  3. 将操作转发到当前同步副本集合中的每个副本,如果有多个副本,则并行执行。
  4. 一旦所有副本都成功地执行了操作并响应了主碎片,主碎片就会对客户端确认请求已经成功完成。

故障处理

在索引过程中,许多事情都可能出错 - 磁盘可能会损坏、节点之间可能断开连接、或某些配置错误可能导致副本上的操作失败,尽管它在主碎片上是成功的,这种情况并不常见,但主碎片必须对其作出响应。

在主碎片失败的情况下,主碎片所在的节点将向主节点发送关于主碎片失败的消息,索引操作将等待(默认情况下最多1分钟),以便主节点将一个副本提升为一个新的主碎片。然后,操作将被转发到新的主碎片进行处理,注意,主节点还监控节点的健康状况,并可能决定主动降级主碎片,这通常发生在持有主碎片的节点由于网络问题与集群隔离时。

在主碎片上成功执行操作之后,当在副本碎片上执行操作时,主碎片必须处理潜在的故障,这可能是由于副本上的实际故障,或者是由于网络问题阻止操作到达副本(或者阻止副本响应)。所有这些都有相同的最终结果:同步副本集合中的一个副本错过一个将要被确认的操作,为了避免违反不变量,主碎片向主节点发送一条消息,请求从同步副本集合中删除有问题的碎片。只有在主节点确认移除碎片后,主碎片才会确认操作,注意,主节点还将指示另一个节点开始构建一个新的碎片副本,以便将系统恢复到健康状态。

在将操作转发到副本时,主碎片将使用副本来验证它仍然是活动的主碎片,如果主碎片由于网络分区(或长时间GC)而被隔离,在意识到主碎片已经被降级之前它可能会继续处理传入的索引操作,来自过期的主碎片的操作将被副本拒绝。当主碎片收到来自副本的拒绝它请求的响应,因为它不再是主碎片时,它会联系主节点,并将会知道它已经被替换了,然后将操作路由到新的主碎片。

如果没有副本会发生什么?

这是一个由于索引配置或仅仅因为所有副本都失败而可能发生的有效场景,在这种情况下,主碎片是在没有任何外部验证的情况下处理操作,这可能看起来有问题,另一方面,主碎片本身不能使其他碎片失败,但请求主节点代表它这样做,这意味着主节点知道主碎片是唯一一个好的副本。因此,我们保证主节点不会将任何其他(过期的)碎片副本提升为新的主碎片,并且主碎片中索引的任何操作都不会丢失,当然,因为此时我们只运行数据的一个副本,所以物理硬件问题可能导致数据丢失。

基础读模型

在Elasticsearch中的读可以是非常轻量的通过ID的查找,也可以是使用复杂聚合的繁重搜索请求,而复杂的聚合会占用大量的CPU资源。主备模型的优点之一是保持所有碎片副本相同(除了飞行的操作之外),因此,一个同步副本就足以服务于读请求。

当节点接收到读请求时,该节点负责将其转发到持有相关碎片的节点、整理响应并响应客户端,我们将该节点称为该请求的协调节点,基本流程如下:

  1. 将读请求解析到相关的碎片,注意,由于大多数搜索将被发送到一个或多个索引,它们通常需要从多个碎片中读,每个碎片代表数据的不同子集。
  2. 从碎片复制组中选择每个相关碎片的活动副本,这可以是主碎片,也可以是副本,默认情况下,Elasticsearch将会在碎片副本之间进行简单的循环负载。
  3. 将碎片级别的读请求发送到选中的副本。
  4. 组合结果和响应,注意,在get by ID查找的情况下,只有一个碎片是相关的,可以跳过这一步。

故障处理

当碎片无法响应读请求时,协调节点将从同一复制组中选择另一个副本,并将碎片级搜索请求发送到该副本,重复的失败会导致碎片副本不可用。在某些情况下,比如_search,Elasticsearch将更喜欢快速响应,尽管会得到部分结果,而不是等待问题得到解决(响应的_shards头中显示了部分结果)。

几个简单的含义

这些基础流中的每一个都决定了Elasticsearch作为读写系统的行为,此外,由于读写请求可以并发执行,这两个基本流相互交互,这有一些内在的含义:

高效的读

  • 在正常操作下,对每个相关复制组执行一次读操作,只有在失败的情况下,同一个碎片的多个副本才会执行相同的搜索。

未确认的读

  • 由于主碎片首先本地索引,然后复制请求,所以并发读可能在确认更改之前就已经看到了更改。

默认为两个副本

  • 这个模型可以在当只维护数据的两个副本时容错,这与基于数量的系统形成对比,其容错的最小副本数为3。

故障

在故障的情况下,以下是可能的情况:

单个碎片可以减慢索引速度

  • 因为主碎片在每次操作期间都要等待同步复制集中的所有副本,所以单个慢碎片可能会减慢整个复制组的速度,这是我们为上述读效率所付出的代价,当然,单个慢碎片也会不幸的减慢被路由到它的搜索。

脏读

孤立的主碎片可以公开不被承认的写操作,这是由这样一个事实引起的:一个孤立的主碎片只有在向其副本发送请求或与主节点接触时才会意识到它是孤立的,这时,操作已经被索引到主碎片中,并且可以被并发读取,Elasticsearch通过每秒ping一次主节点(默认情况下)并在如果不知道主节点的情况下拒绝索引操作来减轻这种风险。

冰山一角

本文档提供了Elasticsearch如何处理数据的高层次概述,当然,在引擎下还有很多事情要做,像主碎片项、集群状态发布和主节点选举等都在保持系统正常运行方面发挥了作用,本文档也没有已知的和重要的bug(关闭和打开),我们认识到Github很难跟上,为了帮助人们保持在这些之上,我们在我们的网站上维护一个专用的弹性页面,我们强烈建议你阅读它。


下一篇:Index API

博弈
2.5k 声望1.5k 粉丝

态度决定一切