1、问题现象描述
假设有shard_local_idf
索引(索引数据见文章末尾)
GET shard_local_idf/_search{"query": {"match": {"title": "xiaomi shouji"}}}
召回结果如下:
"hits" : [{"_index" : "shard_local_idf","_type" : "_doc","_id" : "11","_score" : 1.9924302,"_routing" : "0","_source" : {"title" : "xiaomi tv"}},{"_index" : "shard_local_idf","_type" : "_doc","_id" : "1","_score" : 1.6282079,"_routing" : "1","_source" : {"title" : "xiaomi shouji"}},{"_index" : "shard_local_idf","_type" : "_doc","_id" : "10","_score" : 1.4816045,"_routing" : "1","_source" : {"title" : "xiaomi tv"}}...]
2、原因分析
注意
如果对 相关性评分算法比较陌生,推荐先阅读以下文章: Elasticsearch相关度评分算法(三):BM25(Okapi BM25)[1]
2.1 词频得分(TF)相关性
对于_doc1和_doc11,两个文档词频都是1,他们在同一个索引,所以反词频一定相同,所以单就TF得分而言,这两个词得分肯定相同。
因此,对于上述问题,TF得分为常量,不是问题产生的主要原因。
2.2 反词频得分(IDF)相关性
假设对于搜索词“xiaomi”,两个文档的得分都是 $x$,对于_doc1 而言,由于假设 $idf$ 得分为 $y$ 根据 $idf$ 计算函数可知 $y$ > 0,而对于 _doc11 而言,tv
并未被命中,因此 $idf$ 得分为 0。
那么由此可计算两个文档的最终得分为:_doc1:$x + y$ > _doc11:$x$ 什么是逆文档频率评分(IDF)
2.3 eplain 查看执行计划
通过 explain 计算以下得分,如下
GET shard_local_idf/_search{"explain": true,"query": {"match": {"title": "xiaomi shouji"}}}
对于 query:xiaomi shouji 查询结果如下:
•_doc1:总分:1.4816045(term:xiaomi) + 0.14660348(term:shouji) = 1.6282079•xiaomi:2.2 * 1.4816046 * 0.45454544 = 1.4816045•boost:2.2($k_1$+1)•idf:1.4816046•n:2•N:10•tf:0.45454544•shouji:2.2 * 0.14660348 * 0.45454544 = 0.14660348•boost:2.2($k_1$+1)•idf:0.14660348•tf:0.45454544•_doc11:总分:1.9924302(term:xiaomi) = 1.9924302•xiaomi:2.2 * 1.9924302 * 0.45454544 = 1.9924302•boost:2.2($k_1$+1)•idf:1.9924302•n:1•N:10•tf:0.45454544•tv:0
3、解决方案
3.1 开发和灰度环境或数据量不大的情况
search_type
•dfs_query_then_fetch
:使用从运行搜索的所有分片收集的信息,全局计算分布式词频。虽然此选项提高了评分的准确性,但它增加了每个分片的往返行程,这可能会导致搜索速度变慢。•query_then_fetch
:(默认)为每个运行搜索的分片本地计算分布式词频。我们建议使用此选项进行更快的搜索,但评分可能不太准确。
测试环境设置:search_type
= dfs_query_then_fetch
即表示在计算 $idf$ 的分值的时候,对全局进行计算,而不是 local_shard 。会牺牲一部分性能,换取准确性。这种方式适合于本地开发环境或者测试环境,或者生产环境中数据不对的情况下。
3.2 对于生产环境
对于生产环境,一般分布式数据库数据都不会太少,既然设计了多个分片,必然要考虑海量数据的情况。一般来说用 query_then_fetch
不太合适,会影响检索速度,牺牲用户体验。
生产环境真正的做法是避免分片不均衡,包括分片的大小、节点分片的分配数量、文档的均衡分配等。ES 本身通过 shard reblance 实现分片自动均衡策略,但是如果人工通过 routing 的方式分配数据,务必要保证数据按照某种机制,如分布式哈希表来控制数据的均衡分配,以避免这种情况的产生。
总结:在不了解分布式文档路由原理的前提下,不要随意使用 routing 来指定文档的分配机制。以免挖坑。
GET shard_local_idf/_search?search_type=dfs_query_then_fetch{"explain": true,"query": {"match": {"title": "xiaomi shouji"}}}
4、测试数据
PUT shard_local_idf{"settings": {"number_of_shards": 2,"number_of_replicas": 0}}PUT shard_local_idf/_bulk?routing=1&refresh=true{"index":{"_id":1,"_type":"_doc"}}{"title":"xiaomi shouji"}{"index":{"_id":2,"_type":"_doc"}}{"title":"huawei shouji"}{"index":{"_id":3,"_type":"_doc"}}{"title":"pingguo shouji"}{"index":{"_id":4,"_type":"_doc"}}{"title":"zhongxing shouji"}{"index":{"_id":5,"_type":"_doc"}}{"title":"sanxing shouji"}{"index":{"_id":6,"_type":"_doc"}}{"title":"bodao shouji"}{"index":{"_id":7,"_type":"_doc"}}{"title":"oppo shouji"}{"index":{"_id":8,"_type":"_doc"}}{"title":"lg shouji"}{"index":{"_id":9,"_type":"_doc"}}{"title":"vivo shouji"}{"index":{"_id":10,"_type":"_doc"}}{"title":"xiaomi tv"}PUT shard_local_idf/_bulk?routing=0&refresh=true{"index":{"_id":11,"_type":"_doc"}}{"title":"xiaomi tv"}{"index":{"_id":12,"_type":"_doc"}}{"title":"huawei 12"}{"index":{"_id":3,"_type":"_doc"}}{"title":"pingguo 13"}{"index":{"_id":14,"_type":"_doc"}}{"title":"zhongxing 14"}{"index":{"_id":15,"_type":"_doc"}}{"title":"sanxing 51"}{"index":{"_id":16,"_type":"_doc"}}{"title":"bodao 16"}{"index":{"_id":17,"_type":"_doc"}}{"title":"oppo 17"}{"index":{"_id":18,"_type":"_doc"}}{"title":"lg 18"}{"index":{"_id":19,"_type":"_doc"}}{"title":"vivo 19"}{"index":{"_id":20,"_type":"_doc"}}{"title":"meizu 20"}
References
[1]
Elasticsearch相关度评分算法(三):BM25(Okapi BM25): https://es-cn.blog.csdn.net/article/details/124811138
快来和小伙伴们一起学习更多干货

点击下方“阅读原文”查看更多




