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