索引的路由计算
当索引一个文档的时候,文档会被存储到一个主分片中, Elasticsearch如何知道一个文档应该存放到哪个分片中呢?
肯定不是随机的而是根据以下算法来决定的
shard = hash(routing)% number_of_primary_shards
1) routing值是一个任意字符串,它默认是_id,但也可以自定义
2) 这个routing字符串通过哈希函数生成一个数字,然后除以主切片的数量得到一个余数(remainder),余数的范围永远是0到number_of_primary_shards -1,这个数字就是特定文档所在的分片。
注意事项
创建索引的时候就确定好主分片的数量,并且永远不会改变这个数量,数量的改变将导致上述公式的结果变化,最终会导致我们的数据无法被找到。
文档的写操作
新建、索引和刷除请求都是写(write)操作,它们必须在主分片上成功完成才能复制到相关的复制分片上。
下图是数据写入 P0 主分片的过程,master在这里起到一个协调节点的作用。
详细步骤
下面我们罗列在主分片和复制分片上成功新建、索引或删除一个文档必要的顺序步骤。
1.客户端给 Node1 发送新建、索引或删除请求。
2.节点使用文档的 _id确定文档属于分片0,它转发请求到 Node3,分片0位于这个节点上
3.Node3在主分片上执行请求
4.Node 3保存文档,将数据保存到主分片
5.保存成功后,它转发请求到相应的位于Node1和 Node2的复制节点上
6.当所有的复制节点报告成功, Node3报告成功到请求的节点
7.请求的节点再报告给客户端,客户端接收到成功响应的时候,文档的修改已经被应用于主分片和所有的复制分片
注意事项
把文档存储写入到primary shard,如果设置了index.write.wait_for_active_shards=1,那么写完主节点,直接返回客户端,如果 index.write.wait_for_active_shards=all,那么必须要把所有的副本写入完成才返回客户端。
验证
PUT /customer {"mappings":{"properties":{"name":{"type":"keyword"}}}, "settings":{"index":{"number_of_shards": 1,"number_of_replicas": 2,"write.wait for_active_shards": "a11"}}
}
POST customer/_doc
{"name": "张三"
}
搜索单个文档
根据文档ID如何搜索到文档?
分片或任意一个复制分片被检索。
- 客户端给Node 1发送get请求。
- 节点使用文档的 _id 确定文档属于分片 0 。分片 0 对应的复制分片在三个节点上都有。此时,它转发请求到Node 2。
- Node 2 返回文档(document)给 Node 1 然后返回给客户端。
更新单个文档
更新文档,必须先定位到主分片,修改文档后,再次同步到其他副本中才算完成。
以下是部分更新一个文档的步骤:
1.客户端向 Node 1发送更新请求,发现主分片在 Node 3
2.它将请求转发到主分片所在的 Node 3
3.Node 3从主分片检索文档,修改_source 字段中的JSON,并且尝试重新索引主分片的文档,如果文档已经被另一个进程修改,它会重试步骤3,超过 retry_on_conf1ict 次后放弃。
4.如果 Node 3成功地更新文档,它将新版本的文档并行转发到 Node1和 Node 2上的副本分片,重新建立索引,一旦所有副本分片都返回成功, Node 3 向协调节点也返回成功,协调节点向客户端返回成功。
全文搜索
对于全文搜索而言,文档可能分散在各个节点上,那么在分布式的情况下,如何搜索文档呢?
搜索,分为2个阶段,搜索(query)+取回(fetch)。
搜索
详细步骤
1、客户端发送一个search(搜索)请求给 Node 3创建了一个长度为from+size的空优先级队
2、Node 3 转发这个搜索请求到索引中每个分片的原本或副本。每个分片在本地执行这个查询并且结果将结果放到一个大小为 from+size 的有序本地优先队列里去。
3、每个分片返回document的ID和它优先队列里的所有document的排序值给协调节点Node 3。Node 3把这些值合并到自己的优先队列里产生全局排序结果。
优先级队列
一个 优先队列 仅仅是一个存有 top-n 匹配文档的有序列表,优先队列的大小取决于分页参数 from 和size,如下搜索请求将需要足够大的优先队列来放入100条文档。
GET /_search {"from": 90,"size": 10
}
注意事项
当一个搜索请求被发送到某个节点时,这个节点就变成了协调节点
这个节点的任务是广播查询请求到所有相关分片并将它们的响应整合成全局排序后的结果集合,这个结果集合会返回给客户端。
第一步是广播请求到索引中每一个节点的分片拷贝,查询请求可以被某个主分片或某个副本分片处理,这就是为什么更多的副本(当结合更多的硬件)能够增加搜索吞吐率,协调节点将在之后的请求中轮询所有的分片来分摊负载。
每个分片在本地执行查询请求并且创建一个长度为 from + size 的本地优先队列,也就是说,每个分片创建的结果集足够大,均可以满足全局的搜索请求,分片返回一个轻量级的结果列表到协调节点,它仅包含文档 ID 集合以及任何排序需要用到的值,例如_score
协调节点将这些分片级的结果合并到自己的有序优先队列里,它代表了全局排序结果集合,至此查询过程结
取回
详细步骤
- 协调节点辨别出哪个document需要取回,并且向相关分片发出 GET 请求。
- 每个分片加载document并且根据需要 enrich 它们,然后再将document返回协调节点。
- 一旦所有的document都被取回,协调节点会将结果返回给客户端。
注意事项
协调节点首先决定哪些文档 确实需要被取回.
例如,如果我们的查询指定了{"from":90, "size::l0},最初的90个结果会被丢弃,只有从第91开始的10个结果需要被取回,这些文档可能来自和最初搜索请求有关的一个或者多个甚至全部分片。
协调节点给持有相关文档的每个分片创建一个mu1ti-getrequest ,并发送请求给同样处理查询阶段的分片副本。