读取和写入文档编辑

简介编辑

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

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

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

基本写入模型编辑

Elasticsearch 中的每个索引操作首先使用路由(通常基于文档 ID)解析到副本组。确定副本组后,操作将在内部转发到该组的当前*主分片*。索引的这个阶段称为*协调阶段*。

An example of a basic write model.

索引的下一阶段是在主分片上执行的*主阶段*。主分片负责验证操作并将其转发到其他副本。由于副本可能处于脱机状态,因此主分片不需要复制到所有副本。相反,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 如何处理数据。当然,底层还有很多其他的机制在运作。诸如主分片、集群状态发布和主节点选举等机制都在保持系统正常运行方面发挥着作用。本文档也没有涵盖已知的重要错误(包括已解决的和未解决的)。我们认识到 GitHub 上的信息很难跟进。为了帮助人们及时了解这些信息,我们在网站上维护了一个专门的 弹性页面。我们强烈建议您阅读它。