《Elastic Stack 实战手册》——三、产品能力——3.4.入门篇——3.4.2.Elasticsearch基础应用——3.4.2.4.分布式计分(上) https://developer.aliyun.com/article/1230903
Query 阶段:
1、Elasticsearch 在收到客户端搜索请求后,会由协调节点将请求分发到对应索引的每个
Shard 上。
2、每个 Shard 的 Lucene 实例基于本地 Shard 内的 TF/IDF 统计信息,独立完成 Shard 内的索引匹配和打分(基于上述公式),并根据打分结果完成单个 Shard 内的排序、分页。
3、每个 Shard 将排序分页后的结果集的元数据(文档 ID 和分数,不包含具体的文档内容)返回给协调节点。
4、协调节点完成整体的汇总、排序以及分页,筛选出最终确认返回的搜索结果。
Fetch 阶段:
1、协调节点根据筛选结果去对应 shard 拉取完整的文档数据
2、整合最终的结果返回给用户客户端
分布式打分的权衡
我们再来看一个场景,先重建索引,但是我们将 Shard 建成 3:
#删除已有索引 DELETE /my-index-000001 #创建索引,显示在 settings 中指定3个 shard:"number_of_shards": "3" PUT /my-index-000001 { "settings": { "number_of_shards": "3", "number_of_replicas": "1" }, "mappings": { "properties": { "title": { "type": "text" }, "date": { "type": "date" }, "content": { "type": "text" } } } } #插入记录 PUT /my-index-000001/_doc/1 { "title": "三国志", "date": "2021-05-01", "content": "国别体史书" } PUT /my-index-000001/_doc/2 { "title": "红楼梦", "date": "2021-05-02", "content": "黛玉葬花..." } PUT /my-index-000001/_doc/3 { "title": "易中天品三国", "date": "2021-05-03", "content": "草船借箭、空城计..." } PUT /my-index-000001/_doc/4 { "title": "水浒传", "date": "2021-05-03", "content": "梁山好汉被团灭..." } PUT /my-index-000001/_doc/5 { "title": "三国演义", "date": "2021-05-03", 370 > 三、产品能力 "content": "三国时代,群雄逐鹿..." } 然后再次执行相同的搜索: GET /my-index-000001/_search { "query": { "query_string": { "query": "三国演义" } } } 查看本次搜索结果: { "took" : 6, "timed_out" : false, "_shards" : { "total" : 3, "successful" : 3, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 3, "relation" : "eq" }, "max_score" : 1.6285465, "hits" : [ { "_index" : "my-index-000001", "_type" : "_doc", "_id" : "3", "_score" : 1.6285465, "_source" : { "title" : "易中天品三国", "date" : "2021-05-03", "content" : "草船借箭、空城计..." } }, { "_index" : "my-index-000001", "_type" : "_doc", "_id" : "5", "_score" : 1.1507283, "_source" : { "title" : "三国演义", "date" : "2021-05-03", "content" : "三国时代,群雄逐鹿..." } }, { "_index" : "my-index-000001", "_type" : "_doc", "_id" : "1", "_score" : 0.5753642, "_source" : { "title" : "三国志", "date" : "2021-05-01", "content" : "国别体史书" } } ] } }
搜索结果的排名竟然发生了变化,我们期望排第一的”三国演义”排到了第二,得分为1.1507283,而“易中天品三国”竟然得分1.6285465,跃居第一,这并不符合我们的搜索预期。
通过分析上面的 QUERY_THEN_FETCH 流程,我们不难发现:由于分布式系统天然的割裂性质,每个 shard 无法看到全局的统计信息,所以上述第 2 步中每个 Shard 的打分都是基于本地 Shard 内的 TF/IDF 统计信息来完成的。
在大多数的生产环境中,由于数据量多且在每个 Shard 分布均匀,这种方式是没有问题的。但是在极端情况下(如上例),3 个 shard 中的文档数相差较大,那么 IDF 在 3 个 Shard 中所起到的影响将截然不同,即单个 Shard 内打分汇总后的结果,与全局打分汇总的结果会有相当大的出入,造成我们在靠前的分页,搜到原本应该排名靠后的文档。
这也是分布式打分引入的实际问题,那么如何才能解决这类问题呢?
我们曾在上一小节提到,Elasticsearch 的搜索类型其实有两种,除了上面介绍的 QUERY_THEN_FETCH 之外,还有一种是 DFS_QUERY_THEN_FETCH。
DFS 在这里的意思是分布式频率打分,其思想是提前向所有 Shard 进行全局的统计信息搜集,然后再将这些统计信息,随着查询分发到各个 Shard,让各个 Shard 在本地采用全局 TF/IDF来打分,具体的流程如下:
《Elastic Stack 实战手册》——三、产品能力——3.4.入门篇——3.4.2.Elasticsearch基础应用——3.4.2.4.分布式计分(下) https://developer.aliyun.com/article/1230901