写点什么

ElasticSearch 读写模型 & 数据复制模型

用户头像
yhh
关注
发布于: 2021 年 04 月 01 日

前言

单词对照

1、分片(shard)

2、主分片(primary)

3、副本分片(replication、copy)

4、同步集(in-sync replica copy)

5、副本集(replication group)(同步集是副本集的一个子集)

6、操作(operation)(可指读或者写)

7、索引操作(index)(单指写)


一、介绍

ElasticSearch 中的每个索引都会被分片,而且每个分片都可以有多个副本分片。这些副本分片组成了一个副本集,在文档被添加或删除的时候,它们必须与主分片保持数据同步。如果不能保持同步,那么从一个副本分片读取数据将会和从另一个副本分片读取的数据存在不一致,这种保持分片数据同步和提供分片数据读取的过程,我们把它称作数据复制模型。

ElasticSearch 的数据复制模型是基于主备模式,它在微软的关于 PacificA 算法的研究论文里有很好的描述。这个模型是基于在副本集里有一个单独的分片作为主分片。其他的分片被称作副本分片。主分片作为所有索引操作的主要入口点。它负责校验文档并确保文档是正确的。一旦一个索引操作被主分片接受,主分片也会负责复制这个操作到其他副本分片。

这一节内容主要是对 ElasticSearch 的复制模型给出一个高层次概述,并讨论它对读写操作的各种交互的影响。


二、基本写模型

在 ElasticSearch 里的每一个索引操作,首先被路由到一个副本集,一般是基于文档 id。一旦副本集被确定,操作请求会被内部转发到副本集的当前主分片上。这一阶段被称为协调阶段。

索引操作的下一阶段是主分片处理阶段,发生在主分片上。主分片负责校验这个操作,然后转发这个操作到其他分片上。由于副本分片可能离线了,主分片没有必要同步数据到所有副本分片上。相反,ElasticSearch 维护了一个分片副本的列表,记录接收索引操作的分片信息。这个列表被称作同步副本集,会被维护在 master 节点上。顾名思义,这是一个“好”的分片副本的集合,它们保证已经处理了所有的已对用户确认的索引添加和删除操作。主分片需要负责维护这个不变性,因此必须复制所有的操作到这个集合的每个副本上。

主分片基本处理流程如下:

1、校验输入的操作,比如在有结构性的无效数据时,拒绝该操作(例子:期望一个数字类型的字段,但是传递了一个对象类型数据)

2、在本地执行操作,也就是创建或删除相关索引文档。这里也会校验字段的内容,如果有必要也会拒绝请求。(例子:一个 keyword 字段值太长了,无法在 Lucene 建立索引)

3、转发操作到当前同步集的每一个副本。如果有多个副本,会并行转发操作。

4、一旦所有同步集副本成功处理了该操作,并响应给了主分片,主分片会向客户端确认该操作请求已经成功完成。

每一个同步集副本在本地处理索引操作,所以数据就有了副本。这个索引阶段就是复制阶段。

这些索引阶段(协调阶段、主分片处理阶段、复制阶段)是顺序执行的。为了能够进行内部重试,每一阶段的生命周期都包含了它每一个后续阶段的生命周期。比如,协调阶段,只有等到那些通过不同主分片扩散出去的每一个主分片处理阶段处理完,才算完成。每个主分片处理阶段,只有等到同步集里的副本分片都完成了本地的索引文档操作,并响应了复制请求,才算完成。


三、写失败处理

索引期间,很多事情可能会有异常,比如磁盘可能损坏,节点可能彼此断连,或者配置错误,都能导致一个操作在副本上失败,尽管它在主分片上操作成功了。这些是比较少发生的,但是主分片也需要有应对之法。

在主分片本身失败的情况下,持有主分片的节点会向主节点发送一条信息汇报该情况。当前的索引操作则会等待(默认最多一分钟)主节点提升其他副本分片为新的主分片。这个操作接着会被转发到新的主分片上去处理。需要注意,主节点也会监控节点的健康状态,然后可能也会决定主动降级一个主分片。这一般发生在持有主分片的节点因为网络原因被隔离在集群之外。更多细节参考:。

一旦这个操作在主分片上成功被处理了,当在副本分片上执行这个操作时,主分片就必须处理一些潜在的失败。这可能是副本分片上的一个实际错误导致的,或者由于一些网络问题使得操作请求无法到达副本分片,或者副本分片无法响应。所有这些问题都会导致同样的最终结果:同步集中的一部分副本分片丢失了一个即将被确认的操作。为了避免违反不变性,主分片发送一个消息到主节点,要求把有问题的副本分片移出同步集。只有当这个副本分片被主节点确认移除后,主分片才会确认这个操作。需要注意,主节点也会指示另一个节点开始构建一个新的分片副本,来恢复系统到一个健康的状态。(应该是指 green、yellow、red)

当转发一个操作到副本分片时,主分片会使用副本分片来校验它本身是否仍是活跃的主分片。如果主分片由于网络分区(或者长时间 GC)而被隔离,在意识到它已被降级之前,它仍然可能继续处理输入的索引操作。来自一个脏主分片的操作将会被副本分片拒绝。当这个脏主分片因为不再是主分片,而接收到副本分片的拒绝请求的响应时,这个主分片将会联系主节点,然后了解到自己已被替代。当前的索引操作会被路由到新的主分片上。

当没有副本分片存在时,会发生什么?

这是一个有可能发生的场景,由于索引配置或者仅仅时因为所有副本分片都失败了。在这种情况下,主分片正在处理索引操作时,没有附带任何外部校验,这似乎是有问题的。另一方面,主分片不能让其他分片失败,但可以请求主节点代替它去这样做。这意味着主节点知道主分片是唯一的一个正常的副本。因此,我们得到保证,主节点不会提升其他任何(过期的)分片副本来成为一个新的主分片,任何发生在主分片上的索引操作也不会丢失。当然,既然在那时候我们只在数据的一个单独的副本上运行操作,那么物理硬件的问题就可能会导致数据丢失。可以看 Active shards 章节(本文第八点第 1 小点),有一些改善这种情况的选项。


四、基本读模型

在 ElasticSearch 中的读,可以是通过文档 ID 的非常轻量级的读,也可以是带有会消耗很多 CPU 算力的复杂聚合运算的重量级搜索请求。主备模型的好处之一,就是它保持了所有分片副本的数据一致(除了正在处理中的操作)。严格来说,只有一个副本的同步集,也是足够去服务读取请求的。

当一个节点接收到一个读取请求,这个节点要负责转发这个请求到那些相关分片所在的节点上,整合它们的响应内容,然后回复给客户端。我们称那个节点为请求的协调节点。基本流程如下所示:

1、解析读取请求到相关分片。要注意的是,既然大部分搜索会被发送到一个或多个索引上,它们一般需要从多个分片上读取数据,每一个都代表了数据集的一个不用子集。

2、从分片副本集里,选择每个相关分片的一个活跃副本。选择的可能是主分片或者副本分片。默认的,Elasticsearch 使用 自适应副本选择策略 来选择分片的副本。

3、发送分片级别的读取请求到被选择的副本上。

4、组合结果然后响应。需要注意的是,在通过文档 ID 查询的这种情况下,只有一个分片是相关的,这一步可以跳过。


五、分片读失败处理

当一个分片响应一个读请求失败了,协调节点会发送这个请求到同一复制组的另一个分片副本上。重复的失败可能导致没有可用的分片副本可以响应,

为了确保快速响应,下面这些 API 将会响应部分结果,如果一个或者更多的分片失败的话:

Search

Multi Search

Bulk

Multi Get

包含了部分结果的响应内容仍然提供一个 200 OK 的 Http 状态码。分片失败信息会在响应报文头部的 timed_out 和_shards 字段里展示。


六、一些简单的影响

这些基本流程里的每一个都决定了 Elasticsearch 是怎么作为一个系统去提供读写能力的。此外,既然读写请求能被并发执行,这两个基本流程就会相互影响。这里有一些固有的影响:

1、高效读

在正常的操作下,每个读取操作只被每个相关副本集处理一次。只有在失败的情况下,同一个分片的多个副本会执行同样的搜索。

2、读未确认

既然主分片先在本地处理了索引操作,然后才传播复制请求,那就有可能,在这个索引操作被确认前,一个并发读能够读取到这个索引操作所作出的更改。

3、默认两个副本

这个模型是能容错的,在只维护数据的两个副本的时候。这和基于 quorum 的系统是有差别的,后者要求至少 3 个数据副本才能做到容错。


七、失败场景

在失败的情况下,下面这些情况是可能发生的:

1、一个单独的分片可能减慢索引的速度

由于在每个操作期间,主分片要等待同步集里所有副本完成操作,一个单独的慢分片可能会拖慢整个副本集。这是我们为上面提到的高效读所付出的代价。当然,一个单独的慢分片也会拖慢那些路由到它上面的不幸的那些搜索请求。

2、脏读

一个被隔离的主分片可能会暴露不会被确认的写操作的变更内容。这主要是因为,一个被隔离的主分片,只会在它发送请求到它的副本或者请求到主节点的时候,才意识到它曾经被隔离了。在这时候,这个操作已经被应用到主分片上了,同时它的变更也能被一个并发读读取到。Elasticsearch 采取了一些措施减轻这种问题,通过每秒(默认)ping 主节点,如果找不到主节点就拒绝索引操作。


八、其他

1、虽然本文只是一个概述,但是能对 Elasticsearch 读写过程有个大致的掌握,而不迷失于细节。用来快速复习,最好不过了。

2、wait_for_active_shards,看网上一些文章,对这个参数存在误解,认为是要求 Elasticsearch 写操作时要写入多少个副本才算写成功。但是,看官网文档,个人理解是,这个参数只是在执行写操作之前,对活跃副本做检查,只有满足它要求的活跃副本数量,才会执行写操作,否则直接拒绝。而且写的时候,并不保证就能写成功这么多个活跃的副本。

3、Elasticsearch 同步集(in-sync)的概念挺有意思的,和 kafka 的同步分区副本很像,但是又有很大差别,Elasticsearch 主要是为了提供更高效的读,充分利用副本分片各节点的资源,同时也提高了读一致性,而 kafka 更多的是为了数据安全,防止数据丢失,分区副本本身并不提供读取能力。

4、翻译自 https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-replication.html

5、画图?算了,有点懒。

6、翻译有点渣?是的,就是为了偷懒,毕竟比看英文容易些。

用户头像

yhh

关注

还未添加个人签名 2018.04.26 加入

还未添加个人简介

评论

发布
暂无评论
ElasticSearch读写模型&数据复制模型