# 读写文档

## 介绍

Elasticsearch中的每个索引都[分为分片](../../getting_started/basic_concepts#getting-started-shards-and-replicas)，每个分片可以有多个拷贝。这些拷贝称为复制组，并且在添加或删除文档时必须保持同步。如果我们没有这样做，将导致从一个拷贝读取与从另一个读取相比出现非常不同的结果。保持分片拷贝同步并从中读取的过程就是我们所说的数据复制模型。

Elasticsearch的数据复制模型基&#x4E8E;_&#x4E3B;备模型_，并在Microsoft Research的[PacificA 论文](https://www.microsoft.com/en-us/research/publication/pacifica-replication-in-log-based-distributed-storage-systems/)中得到很好的描述。该模型基于复制组有一个拷贝作为主分片。其他拷贝称为副本分片。主分片作为所有索引操作的主要入口点，负责验证与确保正确。一旦索引操作被主分片接受，主分片负责将操作复制到其他副本。

本节的目的是给出Elasticsearch复制模型的高级概述，并讨论它对写操作和读操作之间的各种交互的影响。

## 基础写模式

Elasticsearch中的每个索引操作首先通过使用[路由](../index_api#index-routing)来解析复制组，通常基于文档ID。一旦确定了复制组，操作将在内部转发到组的当前主分片。主分片负责验证操作并将其转发给其他副本。由于副本可以脱机，因此主分片不需要复制到所有副本。相反，Elasticsearch会维护一个应该接收操作的分片拷贝的列表。该列表称为`in-sync copies`，由主节点维护。顾名思义，这些是一组“好”的分片拷贝，保证已经处理了所有已被确认给用户的索引和删除操作。主分片负责维持这个不变，因此必须将所有操作复制到此集合中的每个副本。

主分片遵循以下基本流程：

1. 验证进入的请求并拒绝无效的结构（示例：预期数字字段但传入的是对象字段）
2. 在本地执行操作，即索引或删除相关文档。这还将验证字段的内容并根据需要拒绝（示例：关键词值太长，无法在Lucene中进行索引）。
3. 转发操作到当前`in-sync copies`集合中的每个副本。如果有多个副本，这是并行完成的。
4. 一旦所有的副本都成功地执行了操作并对主分片作出响应，主分片确认成功地完成了对客户端的请求。

### 失败处理

索引过程中可能会出现许多问题——磁盘可能会损坏、节点可能会断开连接、或者某些配置错误可能导致副本上的操作失败，尽管它在主服务器上成功。这些都是不常见的，但是主分片必须对他们做出回应。

在主分片本身失败的情况下，托管主分片的节点将向主节点发送关于它的消息。索引操作将等待（[默认](../../index_modules#dynamic-index-settings)情况下最多1分钟）主节点将其中一个副本升级为新的主分片。然后将该操作转发到新的主分片处理器。请注意，主节点还监控节点的运行状况，并可能决定主动降级主分片。这通常发生在持有主分片的节点因为网络问题脱离集群。请参阅[这里](#demoted-primary)了解更多详情。

一旦在主分片执行操作成功，那么在复制分片上执行它时，主分片处理潜在的失败。这可能是由于副本上的实际故障或由于网络问题导致操作无法到达副本​​（或防止副本响应）引起的。所有这些都具有相同的最终结果：作为`in-sync`复制集合的一部分的副本将忽略即将被确认的操作。为了避免违反不变量，主分片向主节点发送消息，请求从`in-sync`复制集合中删除有问题的分片。只有一旦主节点确认删除了分片，主分片才能确认操作。请注意，主节点还将指示另一个节点开始构建新的分片副本，以便将系统恢复到正常状态。

在将操作转发到副本时，主分片将使用副本来验证它仍然是主片。如果由于网络分区隔离（或长GC），主分片在意识到已降级之前可能会继续处理传入的索引操作。来自老的主分片的操作将被副本拒绝。因为它不再是主分片，当主分片接收到来自副本拒绝其请求的响应时，所以它将连接到主节点，并将得知它已被替换。然后将操作路由到新的主分片。

> ## 如果没有副本，会发生什么？
>
> 这是一个有效的场景，由于索引配置可能会发生，或者因为所有的副本都失败了。在这种情况下，主分片的处理操作没有任何外部验证，这似乎是有问题的。另一方面，主分片是不能自己失败其他分片，但是要求主节点代表这样做。这意味着主节点知道主分片是唯一的单一的好的拷贝。因此，我们保证，主节点不会将任何其他（过时的）分片拷贝推举为新的主分片，而且主分片的任何索引操作都不会丢失。当然，由于在这一点上，我们只运行单个数据拷贝，因此物理硬件问题可能会导致数据丢失。有关缓解选项，请参阅[“等待活动分片”一节](../index_api#index-wait-for-active-shards)。

## 基础读模式

Elasticsearch中的读取可以通过非常轻量级的ID查找或重量级的具有复杂的聚合的搜索请求，需要有有非凡的CPU功率。主备模式的一个优点是它保持所有分片拷贝相同（异常的操作除外）。因此，单个在线同步的分片就足以服务于读取请求。

当节点接收到读请求时，该节点负责将其转发到保存相关分片的节点，整理响应以及响应客户端。我们称该节点为该请求&#x7684;_&#x534F;&#x8C03;_&#x8282;点。基本流程如下：

1. 将读请求解析到相关分片。请注意，由于大多数搜索将发送到一个或多个索引，它们通常需要从多个分片中读取，每个分片表示数据的不同子集。
2. 从分片复制组中选择每个相关分片的活动拷贝。这可以是主分片或副本。默认情况下，Elasticsearch只会简单的轮训分片拷贝。
3. 将分片级读取请求发送到所选拷贝。
4. 结合结果和回应。请注意，在通过ID查找的情况下，只有一个分片是相关的，并且可以跳过该步骤。

### 失败处理

当分片未能响应读请求时，协调节点将从同一复制组中选择另一个拷贝，并将该分片级搜索请求发送给该拷贝。重复故障可能导致没有分片拷贝可用。在某些情况下，如`_search`，Elasticsearch将更愿意快速响应，尽管部分结果，而不是等待该问题得到解决（部分结果在响应的`_shards`头中指出）。

## 一些简单的含义

每一个这些基本流程的确定了Elasticsearch系统如何表现为读取和写入数据。此外，由于可以同时执行读写请求，所以这两个基本流彼此相互作用。这有一些固有的含义：

**高效读取**

```
在正常操作下，每个相关复制组执行一次读取操作一次。只有在故障条件下，同一个搜索才会在多个拷贝的同一个分片上执行相同的搜索。
```

**读取未确认**

```
由于主分片首先在本地进行索引，然后在向副本发送请求，所以并发读取可以在确认之前已经看到更改。
```

**默认两个拷贝**

```
该模型可以容错，同时只保留两个数据拷贝。这与基于法定人数的系统相反，其中容错的最小份数为`3`。
```

## 失败

在失败的情况下，有以下可能：

单个分片可以减慢索引

```
因为主分片等待每个操作中设置的在线副本中的所有副本，所以单个缓慢的分片可能会减慢整个复制组的速度。这是我们为上述读取效率付出的代价。当然，单个缓慢的分片也会减慢已经发送给它的不幸的搜索。
```

脏读

```
孤立的主分片可以暴露不被确认的写入。这是由于隔离的主分片只有在向其副本发送请求或者向主节点发送请求时才会被隔离。在这一点上，操作已经被索引到主分片并且可以通过并发读取。Elasticsearch通过每秒钟（默认情况下）ping主节点来减轻这种风险，如果没有主节点，则拒绝索引操作。
```

## 冰山提示

本文档提供了Elasticsearch如何处理数据的高级概述。当然，底下还有更多更多的事情。诸如主要术语，集群状态发布和主节点选举等都起到了保持系统运行正常的作用。本文档不包括已知和重要的bug（已关闭和打开）。我们认识到[GitHub](https://github.com/elastic/elasticsearch/issues?q=label%3Aresiliency)很难跟上。为了帮助大家掌握，我们在我们的网站上维护一个专用的[弹性页面](https://www.elastic.co/guide/en/elasticsearch/resiliency/current/index.html)。我们强烈建议您阅读。
