ElasticSearch基础之分布式存储

我们在之前的文章中讨论了ElasticSearch中cluster的创建,也了解了Shard的概念,本文就来聊聊ElasticSearch的数据是如何在各个shard中保存和查询的。

文本和Shared的关系

我们知道index的文本是会保存到一个primary的shard中,那么ElasticSearch是如何知道某个文本是属于哪一个shard的呢?比如说我们要保存文本的时候,是应该把它保存到shard1还是shard2,读取的时候又是应该到shard1中查找还是shard2中查找,这些都是如何决定的呢?

这个问题乍一听起来好像还蛮神秘,其实仔细一想也很简单,只有有一个统一的基于某个id的算法就可以实现了。在ElasticSearch中,它是通过下面这个公式来计算的:

这里routing的值默认就是文本的id(当然也可以自定义),这样一来我们就可以把某一个文本指定到特定的shard中。

从这个公式也可以看出来我们为什么需要对primary shard的数目做一个限制,只允许在index创建的时候设置,而且不允许改变,如果改变了,那么所有的根据这个公式算出来的shard值就会发生变化,之前保存的内容就有可能再也找不到了。

Primary和Replica shards之间的交互

为了我们后面的解释方便,先假设相关的cluster结构如下图所示,也就是说整个cluster由三个node组成,然后有两个primary的shards,每一个primary的shard有两个replica。

写操作

写操作包括新建,index和删除文本等,对写操作来说需要首先在primary shard中完成,然后再拷贝到相应的replica中。简单的流程示意图如下所示:

  1. 客户端发送新建,index或者删除的请求到node1.
  2. Node1会根据文本的id来计算属于哪个shard,假设我们计算后发现这个文本属于shard0,它会把相应的请求指向shard0 primary所在的node,也就是node3.
  3. Node3会在primary shard上执行请求,当请求成功之后会把相应的请求在forward对应的replica所在的节点,也就是node2和node1. 当所有的replica shard都回复成功之后(这里其实是可以设置是同步等还是异步处理),它会向node1返回成功,然后node1在向客户端返回成功的response。

大体的流程就是我们上面所示意的,不过这里还是有几个地方需要注意的:

  1. Replication:就是我们上面提到的如何进行replica的处理,默认的值是sync,也就是说只有replica也处理了相关的请求,才会返回response给相应的节点。另外一个设置是async,也就是说当primary处理完成之后就会立即返回response给相应的节点,当然它也会异步把请求forward给replica的节点,只是说replica的节点何时能处理完成就没有保证了。
  2. Consistency:通常来说,在进行写操作之前primary需要检查一下是否有大多数节点是有效的,这样可以保证这个写不会出问题,比如你不会写到一个网络有问题的节点等。通常我们这里所说的大多数是通过这个算式来计算的:

这里我们可以把consistency设置为1(只有primary),all(所有的primary+replica)或者quorum也就是大多数等。

  • Timeout:ElasticSearch如何判断一个节点是有效的,默认来说是这个超时是一分钟。当然你也可以设置成别的值,比如说50ms,30s等等。

读操作

文本的读相对来说就简单了很多,既可以从一个primary的shard读也可以从一个replica进行读。大概的流程如下所示:

  1. Node1接收到了一个读请求。
  2. Node1根据id计算出来这个文本是在shard0上,我们可以看到三个节点都有shard0的数据,所以都可以被选择,这里随机选择了node2来获取数据。
  3. Node2返回文本给node1,然后node1再返回对应的结果给客户端。

这里的第二步如何选择哪一个节点也是有讲究的,有点类似负载均衡的做法,就是大概的意思还是希望各个节点的访问和处理尽量均匀。比如说可以使用round-robin来轮询。

读写结合的操作

比较常见的读写结合的操作就是进行局部更新(Partial Update)。大概的示意图如下所示:

  1. 客户端把请求发送到node1
  2. 同样的会根据id找到相应的primary shard在node3,把对应的请求转发到node3
  3. Node3会从primary shard中查询文本,改变相应的局部内容,然后在primary中重新index文本。假如有别的process在这个过程中也修改了文本,那么就需要进行conflict的处理。
  4. 当primary处理好了之后,他会把新的版本发给所有的replica的节点,等他们全部处理成功了,会向node1返回成功的response,从而最终返回想的结果给客户端。

Mget和bulk操作

对于类似mget和bulk的操作其实和单个操作比较类似,唯一的差别就是第一个节点会根据shard重新组合这些操作,然后再并行把这些request发送给相应的节点。最后在返回结果的时候还需要进行重新组合,一起返回给客户端。下图是一个mget的请求示意图。

  1. 节点1收到mget的请求。
  2. 节点1会重新根据shard来创建request,并把它们发送给对应的shard,等到这些shard的request返回结果之后,再重新组成response返回给客户端。

总结

本文总结了ElasticSearch中分布式存储操作的内部实现方法,包括了读操作,写操作,读写结合操作以及bulk操作的处理方法。

You may also like...

2 Responses

  1. September 4, 2021

    […] ElasticSearch基础之分布式存储的介绍文章。 […]

  2. September 25, 2021

    […] 我们在之前的《ElasticSearch基础之分布式存储》中详细介绍了ElasticSearch是如何进行分布式的读写(CRUD)。那我们在发送查找请求之后,ElasticSearch是如何进行分布式执行的呢?它其实要比我们前面提到的CRUD操作复杂一些,本文就来详细介绍一下这一方面的知识。 […]

Leave a Reply

Your email address will not be published. Required fields are marked *