跳到主要内容

读写文档

简介

Elasticsearch 中的每个索引都被划分为分片,每个分片可以有多个副本。这些副本被称为复制组,在添加或删除文档时必须保持同步。如果我们做不到这一点,从一个副本读取的结果就会与从另一个副本读取的结果大相径庭。保持分片副本同步并提供读取服务的过程就是我们所说的数据复制模型

Elasticsearch 的数据复制模型基于主备份模型,在微软研究院的 PacificA 论文中有详细描述。该模型的基础是复制组中的单个副本作为主分片。其他副本称为副本分片。主分片是所有索引操作的主要入口。它负责验证这些操作并确保其正确性。一旦主分区接受了索引操作,主分区还负责将该操作复制到其他副本。

本节旨在对 Elasticsearch 复制模型进行高层概述,并讨论它对写操作和读操作之间各种交互的影响。

基本写模型

Elasticsearch 中的每个索引操作都会首先通过路由(通常基于文档 ID)解析到一个复制组。一旦确定了复制组,操作就会在内部转发到该组的当前主分区。索引编制的这一阶段被称为协调阶段

索引的下一个阶段是在主分区上执行的主分区阶段。主分区负责验证操作并将其转发给其他副本。由于副本可能处于离线状态,因此主分片不需要复制到所有副本。相反,Elasticsearch 会维护一个应接收操作的分片副本列表。该列表称为同步副本,由主节点维护。顾名思义,这些是一组 "良好 "的分片副本,它们保证已经处理了所有向用户确认的索引和删除操作。主分片负责维护这一不变性,因此必须将所有操作复制到这一组中的每个副本。

主分区遵循以下基本流程

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

每个同步副本在本地执行索引操作,以便拥有一个副本。这个索引阶段就是副本阶段

这些索引阶段(协调、主要和副本)都是顺序进行的。为实现内部重试,每个阶段的生命周期都包含后续每个阶段的生命周期。例如,协调阶段在每个主阶段(可能分布在不同的主分片上)完成之前不会完成。在同步副本完成本地文档索引并响应副本请求之前,每个主阶段都不会完成。

失败处理

在索引编制过程中,很多事情都可能出错:磁盘可能损坏,节点之间可能断开连接,或者某些配置错误可能导致副本上的操作失败,尽管该操作在主节点上是成功的。这些情况并不常见,但主节点必须对此做出响应。

如果主节点本身发生故障,托管主节点的节点将向主节点发送相关消息。索引操作将等待(默认情况下最长 1 分钟)主节点将其中一个副本提升为主节点。然后,操作将被转发到新的主服务器进行处理。请注意,主节点还会监控节点的健康状况,并可能决定主动降级主节点。这种情况通常发生在持有主节点的节点因网络问题与集群隔离时。更多详情,参阅此处

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

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

如果没有副本会怎样?

这是一种有效的情况,可能是由于索引配置,也可能仅仅是因为所有副本都失败了。在这种情况下,主分片会在没有任何外部验证的情况下处理操作,这看起来可能会有问题。另一方面,主分片无法自行使其他分片失效,只能请求主分片代其失效。这意味着主分片知道主分片是唯一的一个良好副本。因此,我们可以保证主分区不会将任何其他(过时的)分区副本提升为新的主分区,并且任何索引到主分区的操作都不会丢失。当然,由于此时我们只使用单一数据副本,物理硬件问题可能会导致数据丢失。参阅活动分片了解一些缓解方案。

基本读取模型

Elasticsearch 中的读取可以是非常轻量级的 ID 查找,也可以是需要消耗大量 CPU 性能的复杂聚合搜索请求。主备份模型的优点之一是,它能使所有分片副本保持一致(飞行中的操作除外)。因此,一个同步副本就足以满足读取请求。

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

  1. 将读取请求解析到相关分片。请注意,由于大多数搜索都会发送到一个或多个索引,因此通常需要从多个分片读取数据,每个分片代表不同的数据子集。
  2. 从分片复制组中选择每个相关分片的活动副本。这可以是主副本,也可以是副本。默认情况下,Elasticsearch 使用自适应副本选择来选择分片副本。
  3. 向所选副本发送分片级读取请求。
  4. 合并结果并做出响应。请注意,在通过 ID 查找获取的情况下,只有一个分片是相关的,因此可以跳过这一步。

分片失败

当分片无法响应读取请求时,协调节点会将请求发送到同一复制组中的另一个分片副本。重复故障会导致没有可用的分片副本。 为确保快速响应,如果一个或多个分片发生故障,以下 API 将响应部分结果:

包含部分结果的响应仍提供 200 OK HTTP 状态代码。分片失败由响应头的 timed_out_shards 字段表示。

几个简单的影响

这些基本流程中的每一个都决定了 Elasticsearch 作为一个系统在读取和写入时的行为方式。此外,由于读取和写入请求可以同时执行,因此这两个基本流程会相互影响。这就产生了一些固有的影响:

高效读取

在正常运行情况下,每个相关复制组只执行一次读操作。只有在故障情况下,同一分片的多个副本才会执行相同的搜索。

读取未确认

由于主分区首先在本地建立索引,然后复制请求,因此并发读取有可能在确认之前就已看到更改。

默认双副本

这种模式可以容错,同时只维护两个数据副本。这与基于法定人数的系统形成鲜明对比,后者的容错副本最少为 3 份。

失败

在故障情况下,可能出现以下情况:

单个分区会降低索引速度

由于主分区在每次操作过程中都会等待同步副本集中的所有副本,因此单个慢分区会拖慢整个复制组的速度。这就是我们为上述读取效率付出的代价。当然,单个慢分区也会拖慢路由到它的不幸搜索。

脏读

孤立的主分区可能会暴露出不被确认的写入。造成这种情况的原因是,被隔离的主分片只有在向其副本发送请求或与主分片联系时才会意识到自己被隔离了。此时,操作已被编入主索引,可以通过并发读取进行读取。Elasticsearch 通过每秒 ping 一次主索引(默认情况下)以及在不知道主索引的情况下拒绝索引操作来降低这种风险。

冰山一角

本文档提供了 Elasticsearch 如何处理数据的高层概述。当然,引擎盖下还有更多工作要做。主条件、集群状态发布和主选举等都对系统的正常运行起着重要作用。本文档也不包括已知和重要的 bug(包括已关闭和开放的)。我们认识到 GitHub 很难跟上时代的步伐。为了帮助大家了解这些问题,我们在网站上维护了一个专门的弹性页面。我们强烈建议您阅读该页面。

原文链接