欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 社会 > 分布式全文检索引擎ElasticSearch-文档的CRUD原理

分布式全文检索引擎ElasticSearch-文档的CRUD原理

2024/12/31 1:01:26 来源:https://blog.csdn.net/qq_46248151/article/details/144508847  浏览:    关键词:分布式全文检索引擎ElasticSearch-文档的CRUD原理

一、文档的存储原理

在索引库中,我们要将一个文档存储下来,由于索引是进行分片的,那么我们的一个文档来了,该存储到哪一个分片中呢?

首先这肯定不会是随机的,否则将来要获取文档的时候我们就不知道从何处寻找了,实际上,这个过程是根据下面这个算法决定的:

shard = hash(routing) % number_of_primary_shards
  • routing值是一个任意字符串,它默认是_id,但也可以自定义。
  • 这个routing字符串通过哈希函数生成一个数字,然后除以主切片的数量得到一个余数(remainder),余数的范围永远是0到number_of_primary_shards - 1,这个数字就是特定文档所在的分片。

通过上面的公式,我们理解并且也需要记住一个重要的规律:

创建索引的时候就确定好主分片的数量,并且永远不会改变这个数量,数量的改变将导致上述公式的结果变化,最终会导致我们的数据无法被找到。

二、文档的写操作原理

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

下图是数据写入P0主分片的过程,master在这里起到一个协调节点的作用

一个写操作的完整过程

我们来看一下详细执行流程 

  1. 客户端给 Node 1 发送新建、索引或删除请求。
  2. 节点使用文档的_id确定文档属于分片0,它转发请求到 Node 3 ,分片0位于这个节点上。
  3. Node 3 在主分片上执行请求
  4. Node 3保存文档,将数据保存到主分片
  5. 保存成功后,它转发请求到相应的位于 Node 1 和 Node 2 的复制节点上
  6. 当所有的复制节点报告成功, Node 3 报告成功到请求的节点
  7. 请求的节点再报告给客户端,客户端接收到成功响应的时候,文档的修改已经被应用于主分片和所有的复制分片

注意:

把文档存储写入到主分片,如果设置了index.write.wait_for_active_shards=1,那么写完主节点,直接返回客户端(然后异步再去同步数据到副本分片),如果 index.write.wait_for_active_shards=all,那么必须要把所有的副本写入完成才返回客户端

三、文档的读操作原理

1、搜索单条文档

我们根据文档ID查询的时候ES是如何搜索到我们的文档的呢

  1. 客户端给 Node 1 发送get请求。
  2. 节点使用文档的_id确定文档属于分片0,对应的分片在三个节点上都有,此时它转发请求到Node2
  3. Node 2 返回文档(document)给 Node 1 然后返回给客户端

注意:

对于读请求,为了平衡负载,请求节点会为每个请求选择不同的分片——负载均衡

        一个被索引的文档已经存在于主分片上却还没来得及同步到副本分片上(同时,读请求也是打到了副本分片),这时副本分片会报告文档未找到,如果查询主分片则会成功返回文档,这种情况下会产生读写不一致的情况。

        由于可能存在主分片的数据还没同步到 副本分片上的情况,所以客户端可能查询到旧的数据,我们可以做相应的调整,保证读取到最新的数据。

2、更新单条文档

更新文档,必须先定位到主分片,修改文档后,再次同步到其他副本中才算完成。

  1. 客户端向 Node 1 发送更新请求,发现主分片在Node 3
  2. 它将请求转发到主分片所在的 Node 3
  3. Node 3 从主分片检索文档,修改 _source 字段中的 JSON ,并且尝试重新索引主分片的文档, 如果文档已经被另一个进程修改,它会重试步骤 3(自旋抢锁) ,超过 retry_on_conflict 次后放弃。
  4. 如果 Node 3 成功地更新文档,它将新版本的文档并行转发到 Node 1Node 2 上的副本分片,重新建立索引, 一旦所有副本分片都返回成功, Node 3 向协调节点也返回成功,协调节点向客户端返回成功。(所以更新操作只要当所有节点都更新成功,才能算作更新成功)

注意:

当主分片把更改转发到副本分片时, 它不会转发更新请求,相反,它转发完整文档的新版本

        注意,这些更改将会异步转发到副本分片,并且不能保证它们以发送它们相同的顺序到达,如果Elasticsearch仅转发更改请求,则可能导致更新顺序错误,导致文档更新结果错误。

3、全文检索

对于全文检索而言,文档可能分散在各个节点上,同时也没有id进行路由(就算考虑路由的方式,那也不可能有多少个id就计算几次路由),那么在分布式的情况下,如何搜索文档呢?
搜索,分为2个阶段,搜索(query)+取回(fetch)

搜索阶段

在初始 查询阶段 时, 查询会广播到索引中每一个分片拷贝(主分片或者副本分片), 每个分片在本地执行搜索并构建一个匹配文档的 优先队列。

  • 客户端发送一个 search(搜索) 请求发送给 Node 3 , 他会创建了一个长度为from+size 的空优先级队列
  • Node 3 转发这个搜索请求到索引中每个分片的主分片或副本分片,每个分片在本地执行这个该查询并且结果将结果存储到一个大小为from+size(limit 1000,10 那么每个分片就会查询从0-1010的数据出来,这就是from+size的原因)的本地有序优先队列里去。
  • 每个分片返回documentID和该节点优先队列里的所有document的排序值给协调节点 Node 3,而Node 3 会把这些值合并到自己的优先队列里产生全局排序结果。

那什么是优先级队列呢?

一个 优先队列 仅仅是一个存有 top-n 匹配文档的有序列表,优先队列的大小取决于分页参数 from 和 size,如下搜索请求将需要足够大的优先队列来放入100条文档

GET /_search
{"from": 90,"size": 10
}

注意:

 当一个搜索请求被发送到某个节点时,这个节点就变成了协调节点

  1. 这个节点的任务是广播查询请求到所有相关分片并将它们的响应整合成全局排序后的结果集合,这个结果集合会返回给客户端。
  2. 第一步是广播请求到索引中每一个节点的分片拷贝, 查询请求可以被某个主分片或某个副本分片处理,这就是为什么更多的副本(当结合更多的硬件)能够增加搜索吞吐率, 协调节点将在之后的请求中轮询所有的分片来分摊负载。
  3. 每个分片在本地执行查询请求并且创建一个长度为 from + size 的本地优先队列,也就是说,每个分片创建的结果集足够大,均可以满足全局的搜索请求,分片返回一个轻量级的结果列表到协调节点,它仅包含文档 ID 集合以及任何排序需要用到的值,例如 _score 
  4. 协调节点将这些分片级的结果合并到自己的有序优先队列里,它代表了全局排序结果集合,至此查询过程结束。

取回阶段

查询阶段标识哪些文档满足搜索请求,但是我们仍然需要取回这些文档

  1. 协调节点辨别出哪个document需要取回,并且向相关分片发出 GET 请求。
  2. 每个分片加载document并且根据需要丰富它们,然后再将document返回协调节点。
  3. 一旦所有的document都被取回,协调节点会将结果返回给客户端。

注意:

协调节点首先决定哪些文档确实需要被取回。

  • 例如,如果我们的查询指定了 { "from": 90, "size": 10 } ,最初的90个结果会被丢弃,只有从第91个开始的10个结果需要被取回,这些文档可能来自和最初搜索请求有关的一个或者多个甚至全部分片。
  • 协调节点给持有相关文档的每个分片创建一个 multi-get request ,并发送请求给同样处理查询阶段的分片副本

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com