ES分布式文档更新

Published: 28 Oct 2019 Category: ES

一、分布式的文档管理

1.1 路由规则

当你索引一个文档,它被存储在单独一个主分片上。根据一个简单的算法决定:

shard = hash(routing) % number_of_primary_shards

routing值是一个任意字符串,它默认是_id,但也可以自定义。

这也解释了为什么主分片的数量只能在创建索引时定义且不能修改:如果主分片的数量在未来改变了,所有先前的路由值就失效了,文档也就永远找不到了。

1.2 主分片和复制分片

一个索引可以有多个主分片,每个主分片可以有多个复制分片。相同的分片不会放在同一个节点上。

复制分片可以满足读需求,写需求要放在主分片上。

1.3 新建、索引和删除

新建、索引和删除请求都是写(write)操作,它们必须在主分片上成功完成才能复制到相关的复制分片上。

下面我们罗列在主分片和复制分片上成功新建、索引或删除一个文档必要的顺序步骤:

  1. 客户端给Node 1发送新建、索引或删除请求。
  2. 节点使用文档的_id确定文档属于分片0。它转发请求到Node 3,分片0位于这个节点上。
  3. Node 3在主分片上执行请求,如果成功,它转发请求到相应的位于Node 1和Node 2的复制节点上。当所有的复制节点报告成功,Node 3报告成功到请求的节点,请求的节点再报告给客户端。

复制默认的值是sync。这将导致主分片得到复制分片的成功响应后才返回。

如果你设置replication为async,请求在主分片上被执行后就会返回给客户端。它依旧会转发请求给复制节点,但你将不知道复制节点成功与否。 上面的这个选项不建议使用。默认的sync复制允许Elasticsearch强制反馈传输。async复制可能会因为在不等待其它分片就绪的情况下发送过多的请求而使Elasticsearch过载。

1.4 检索

文档能够从主分片或任意一个复制分片被检索。

对于读请求,为了平衡负载,请求节点会为每个请求选择不同的分片——它会循环所有分片副本。

1.5 局部更新文档

update API 结合了之前提到的读和写的模式。

下面我们罗列执行局部更新必要的顺序步骤:

  1. 客户端给Node 1发送更新请求。
  2. 它转发请求到主分片所在节点Node 3。
  3. Node 3从主分片检索出文档,修改_source字段的JSON,然后在主分片上重建索引。如果有其他进程修改了文档,它以retry_on_conflict设置的次数重复步骤3,都未成功则放弃。
  4. 如果Node 3成功更新文档,它同时转发文档的新版本到Node 1和Node 2上的复制节点以重建索引。当所有复制节点报告成功,Node 3返回成功给请求节点,然后返回给客户端。

基于文档的复制

当主分片转发更改给复制分片时,并不是转发更新请求,而是转发整个文档的新版本。记住这些修改转发到复制节点是异步的,它们并不能保证到达的顺序与发送相同。如果Elasticsearch转发的仅仅是修改请求,修改的顺序可能是错误的,那得到的就是个损坏的文档。

1.6 批量请求

mget和bulk API与单独的文档类似。差别是单个请求中,节点知道每个文档所在的分片。

它把多文档请求拆成每个分片的对文档请求,然后转发每个参与的节点。一旦接收到每个节点的应答,然后整理这些响应组合为一个单独的响应,最后返回给客户端。

1.6.1 为什么bulk API需要带换行符的奇怪格式,而不是像mget API一样使用JSON数组?

批量中每个引用的文档属于不同的主分片,每个分片可能被分布于集群中的某个节点上。这意味着批量中的每个操作(action)需要被转发到对应的分片和节点上。

如果每个单独的请求被包装到JSON数组中,那意味着我们需要:

  • 解析JSON为数组(包括文档数据,可能非常大)
  • 检查每个请求决定应该到哪个分片上
  • 为每个分片创建一个请求的数组
  • 序列化这些数组为内部传输格式
  • 发送请求到每个分片

这可行,但需要大量的RAM来承载本质上相同的数据,还要创建更多的数据结构使得JVM花更多的时间执行垃圾回收。

取而代之的,Elasticsearch则是从网络缓冲区中一行一行的直接读取数据。它使用换行符识别和解析action/metadata行,以决定哪些分片来处理这个请求。

这些行请求直接转发到对应的分片上。这些没有冗余复制,没有多余的数据结构。整个请求过程使用最小的内存在进行

REF

分布式增删改查