3.4.2.4.分布式计分
创作人:赵震一
什么是打分
搜索引擎中的搜索与数据库中,常规的 SELECT 查询语句,都能帮你从一大堆数据中,找到匹配某个特定关键字的数据条目,但是这两者最大的区别在于,搜索引擎能够基于查询和结果的相关性,帮你做好结果集排序,即搜索引擎会将它认为最符合你查询诉求的数据条目,放在最前面,而数据库的 SELECT 语句却做不到。
那么搜索引擎是怎么做到的呢?其关键在于打分,搜索引擎在完成关键字匹配后,会基于一定的机制对每条匹配的数据(后称文档)进行打分,得分高的文档表示与本次查询相关度高,就会在最后的结果列表中排在靠前的位置,反之则排名靠后,从而帮助你快速找到你最想要的数据。
下面,我们来向 Elasticsearch 插入一些索引数据:
#删除已有索引 DELETE /my-index-000001 #创建索引,显示在 settings 中指定2个 shard:"number_of_shards": "2" PUT /my-index-000001 { "settings": { "number_of_shards": "2", "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 363 > 三、产品能力 { "title": "水浒传", "date": "2021-05-03", "content": "梁山好汉被团灭..." } PUT /my-index-000001/_doc/5 { "title": "三国演义", "date": "2021-05-03", "content": "三国时代,群雄逐鹿..." } 接下去,我们采用关键词“三国演义”进行搜索: GET /my-index-000001/_search { "query": { "query_string": { "query": "三国演义" } } }
查看一下返回记录的排序以及打分情况:
{ "took" : 4, "timed_out" : false, "_shards" : { "total" : 2, "successful" : 2, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 3, "relation" : "eq" }, "max_score" : 3.1212955, "hits" : [ { "_index" : "my-index-000001", "_type" : "_doc", "_id" : "5", "_score" : 3.1212955, "_source" : { "title" : "三国演义", "date" : "2021-05-03", "content" : "三国时代,群雄逐鹿..." } }, { "_index" : "my-index-000001", "_type" : "_doc", "_id" : "1", "_score" : 0.7946176, "_source" : { "title" : "三国志", "date" : "2021-05-01", "content" : "国别体史书" } }, { "_index" : "my-index-000001", "_type" : "_doc", "_id" : "3", "_score" : 0.59221494, "_source" : { "title" : "易中天品三国", "date" : "2021-05-03", "content" : "草船借箭、空城计..." } } ] } }
可以看到 title 为“三国演义”的文档排在第一,分数( _score )是3.1212955,其次是”三国志”,分数是0.7946176,最后是“易中天品三国”,分数是0.59221494,其余没有匹配的文档则没有出现,该搜索结果即打分排名基本符合预期。“三国志” 的得分较 ”易中天品三国” 更高的原因是因为 “三国志” 词语较短,因为在Lucene的打分逻辑中,匹配的Field的长度也会干预得分,具体的打分机制见下一小节对打分机制的讲解。
Elasticsearch 搜索的打分机制
众所周知,Elasticsearch 是以 Lucene 作为其搜索引擎技术的核心基石的。为了适应大数据时代的搜索需求,Elasticsearch 对 Lucene 最大的增强在于,将原本的单机搜索能力扩展到了分布式的集群规模能力,即将原本单机无法支撑的索引数据,水平切分成多个可以独立部署在不同机器上的 Shard,每个 Shard 由独立的 Lucene 实例提供服务,从而以集群的形式对外提供搜索服务。
因此,为了便于理解 Elasticsearch 的分布式搜索的打分机制,我们先来简单回顾下单机情况下 Lucene 是如何打分的。
Lucene 打分机制
当我们向 Lucene 某个索引提交搜索请求后,Lucene 会基于查询完成匹配,并得到一个文档结果集,然后默认基于以下的评分公式,来对结果集中的每个条目计算相关度(评分公式可以基于配置调整)。
其中 q 表示查询,d 表示当前文档,t 表示 q 中的词条,tf(t in d) 是计算词条 t 在文档 d 中的词频,idf(t) 是词条t在整个索引中的逆文档频率。
我们介绍一下最关键的两个概念,即词频( TF )和逆文档频率( IDF )。
词频( TF ):词条在文档中出现的次数
基于特定的 q 和文档 d 来说,词条 t 代指 q 分词后的其中一个词条,t 的词频指该 t 词条在文档 d 中的出现次数,出现次数越多,表示该文档相对于该词关联度更高。
逆文档频率( IDF ):在同一索引中存在该词条的文档数的倒数
包含某个词条的文档数越多,说明这个词条的词频在整个索引中的影响力越弱。
该公式中该有两个关键项。
词条加权( t.getBoost()): 搜索时的一个加权因子,词条 t 在特定查询 q 中的加权系数。
Norm加权( norm(t,d) ): 索引时的一个加权因子,取决于匹配字段包含的 Token 长度以及当前文档 d 的平均 Token 长度,越短的匹配字段得分越高。
通过对 Norm 加权的了解,便能够解答上一小节最后为什么"三国志"得分高于”易中天品三国”了。
我们已经对上述评分公式各项作了讲解,可以了解到,一旦给定查询 q 和文档 d,其得分即为查询中每个词条 t 的得分总和。
而每个词条的得分,一个主要部分是该词条在文档 d 中的词频 ( TF ) 乘以逆文档频率 ( IDF )的平方。即词条在文档中出现的频率越高,则得分越高。而索引中存在该词条的文档越少,逆文档频率则越高,表示该词条越罕见,那么对应的分数也将越高。
回顾完 Lucene 的打分机制,我们再回过来看下 Elasticsearch 的搜索及打分机制。
Elasticsearch 打分机制
Elasticsearch 的搜索类型有两种,默认的称为 QUERY_THEN_FETCH。顾名思义,它的搜索流程分为两个阶段,分别称之为 Query 和 Fetch。
我们来看下 QUERY_THEN_FETCH 的流程:
《Elastic Stack 实战手册》——三、产品能力——3.4.入门篇——3.4.2.Elasticsearch基础应用——3.4.2.4.分布式计分(中) https://developer.aliyun.com/article/1230902