Elasticsearch 原理(下) -- 集群节点与分片的组织与读写原理

2022-06-27 14:24:11 浏览数 (1)

1. 引言

上一篇文章中,我们介绍了 ElasticSearch 的文档存储结构与索引的数据结构。 Elasticsearch 原理(上) — 文档存储结构与索引数据结构

但 ElasticSearch 的卓越性能并不仅仅是来源于其索引与文档压缩所带来的,更为重要的,是 elasticsearch 的集群功能。 本文,我们就来详细介绍一下 elasticsearch 是怎么协调整个集群的工作的,又是如何让整个集群发挥高可用和高性能的呢。

如果你正在使用 ElasticSearch 或者对 ElasticSearch 的原理感兴趣,推荐下面的几本书:

1.1. 集群的节点和分片

关于 elasticsearch 的集群,我们之前已经有过一些介绍

其中已经介绍过节点与分片的概念,下面我们把这些概念再系统详细的介绍一遍。

2. 节点

每个 ElasticSearch 实例就是一个节点,多个节点相互连接协作就组成了一个集群。 ElasticSearch 集群中的每个节点是以 HTTP 协议进行数据请求和传输的,集群中所有节点都知道其他节点,从而将请求转发到适当的节点进行处理,具体的处理流程下文我们再来介绍。

ElasticSearch 集群中的节点主要有以下 5 种:

  1. 主节点
  2. 数据节点
  3. 客户端节点
  4. 部落节点
  5. 协调节点

2.1. 主节点 Master Node

ElasticSearch 集群中最为常见的节点,只要将 node.master 设置为 true(也可以不进行设置,因为节点默认为主节点) 主节点负责维护整个集群的状态,并且在节点加入或者离开集群时重新分片、创建或删除索引。 一个集群必须有主节点来进行协调工作,可以通过配置项 discovery.zen.minimum_master_nodes 指定集群最少拥有的主节点个数,这是一个常见的用来避免脑裂问题的配置,可以参看: 如何防止 Elasticsearch 脑裂问题

2.2. 数据节点 Data Node

顾名思义,数据节点就是用来存储数据的节点,通过 node.data 设置为 true 可以将节点设置为数据节点,和主节点的配置一样,ElasticSearch 的节点默认都是数据节点,你可以不进行配置。 数据节点由于数据的大量存储、写入和读取,对机器性能、CPU、内存、IO 要求都很高,为了保障集群的正常工作,将主节点与数据节点分离是保障集群健康运行的一个非常重要的原则。 即让所有的数据节点配置:

node.master: false node.data: true

而让所有的主节点配置:

node.master: true node.data: false

2.3. 客户端节点 Client Node

如果将 node.master 和 node.data 都配置为 false,那么这个节点就既不是主节点也不是数据节点,这样的节点我们称之为“客户端节点”。 客户端节点负责在接到请求后将请求路由到合适的数据节点来处理,以及分发索引操作。 在一个大型的集群中,将若干个客户端节点组成整个集群的负载均衡层,是非常有利的设计。

2.4. 部落节点 Tribe Node

ElasticSearch 同样支持将多个集群进行联结,他们之间联结的纽带就是“部落节点”。 通过下面的配置,部落节点可以将多个集群状态合并成一个全局集群的状态:

tribe: t1: cluster.name: cluster_one t2: cluster.name: cluster_two

t1、t2 作为标识符可以取任意名字。 此后,部落节点可以同时对两个集群进行读写操作,他保存了多个集群中每个分片的信息,因此他可以知道任何请求需要转发到哪个集群的哪个分片从而可以实现读写操作的顺利进行。 但部落节点需要维护大量分片的信息,一旦总分片数量过大(1000 以上),对集群中大量分片的操作与协调将耗费过多的性能与耗时,所以合理设置集群规模与分片数量是非常重要的。

2.5. 协调节点 coordinating node

上面介绍了四种 ElasticSearch 集群中的节点类型,他们中任何一个节点都可以作为“协调节点”,所谓的“协调节点”,就是接收客户端命令后将命令路由到指定节点来执行的节点。 如果集群中存在客户端节点,那么随机选取客户端节点担任协调节点的角色是很好的选择,否则可以在整个集群中选择任意节点作为协调节点。

3. 分片

分片的概念就容易理解多了,单台机器可能无法存储大量的数据,即便是机器拥有这么大量的存储空间,将数据打散到多个分片中也有利于系统吞吐量和性能的提升。 同时,也需要考虑数据的拆分备份和容灾。 ElasticSearch 的数据就是通过将多个分片分布到多台服务器上,每个分片建立独立的索引,从而实现了数据的并发读写、备份容灾、横向扩展等的集群特性。 ElasticSearch 的分片分为两种:

  1. 主分片
  2. 复制分片

复制分片是主分片的一个副本,用来防止数据丢失,复制分片只提供数据读取,当集群有数据写入和更新时,这些请求全部由主分片来完成,并同步到复制分片中,下文我们将详细介绍。 通过下面两个配置,可以设置集群中主分片和复制分片的数量:

index.number_of_shards: 5 index.number_of_replicas: 5

也可以通过请求动态调整这两个参数:

PUT /blogs { "settings" : { "number_of_shards" : 3, "number_of_replicas" : 1 } }

4. ElasticSearch 数据写入过程

上面已经提到了 ElasticSearch 的数据写入过程:

  1. 客户端选取一个节点作为协调节点发送数据写入请求
  2. 协调节点解析请求,确定 document 所在的主分片,将请求路由到主分片所在数据节点
  3. 数据节点处理请求并更新主分片数据后,将数据同步到对应的复制分片
  4. 协调节点在所有主分片和复制分片全部完成数据写入后,返回成功响应给客户端

以上四步完成后,客户端就可以从集群读取到最新的数据了,这个过程非常短,通常最多只需要一秒的时间,这就是 ElasticSearch 的 NRT 特性(Near Realtime),也就是近实时。 那么,这个特性是如何实现的呢? 众所周知,磁盘操作是非常耗时的,因此,ElasticSearch 每次数据写入操作都会先写入到内存 buffer 中,同时记录位于磁盘上的 translog,随即认为该操作成功。 ElasticSearch 的后台进程每秒钟会将 buffer 中的数据写入到一个 segment file 中,保存在操作系统级的 os cache 中,与此同时建立基于这个 sigment file 数据的数据索引,这个操作的消耗也很小,但这一步操作后,其他进程就已经可以读取到该文件内容了。 当 translog 容量满或者达到 30 分钟,ElasticSearch 会执行 fsync 强制将 os cache 中所有的 sigment file 汇集成一个 commit point 刷入磁盘中并清空 translog。 如果这一过程中的某一步发生掉电,ElasticSearch 就可以在重新启动时通过 translog 中记录的内容实现数据的恢复操作,但事实上,为了提升数据的写入性能,translog 是每 5 秒进行一次刷入磁盘操作,这就造成在极端情况下,ElasticSearch 掉电恢复后可能会丢失最多 5 秒的数据。

5. ElasticSearch 的更新与删除

与数据写入类似,数据的删除也是通过上述流程实现的,ElasticSearch 仅通过写入一个 .del 文件记录分片中被删除的 docid 即完成了数据的删除。 对分片进行检索时,引擎会在搜索结果之上通过 .del 文件进行一次过滤,从而去除 deleted 状态的 doc。 ElasticSearch 通过定期清理,在磁盘中删除对应的 doc 并清除 .del 文件实现集群中已删除数据的物理清除,从而保证了集群性能。 而更新操作,则是在删除原 doc 的基础上创建新的 doc 实现的。

6. ElasticSearch 数据的读取

与写入过程相比,读取过程就显得比较简单了:

  1. 客户端发送请求到一个协调节点
  2. 协调节点将搜索请求路由到所有 docid 所在的主分片或复制分片所在节点
  3. 每个节点对所有分片并发执行搜索工作,并将作为结果的 docid 集返回协调节点
  4. 由协调节点进行数据的合并、排序、分页等操作,产出最终 docid 结果集
  5. 协调节点通过 docid 结果集到对应集群节点中请求 doc 数据,并汇总返回客户端

7. 参考资料

https://www.elastic.co/blog/tribe-nodes-and-cross-cluster-search-the-future-of-federated-search-in-elasticsearch。

0 人点赞