关于ElasticSearch搜索效果的问题分析

本文主要讨论两个问题:

  1. 如何聚合多个节点或分片的数据生成返回结果?
  2. ES是如何将相关度高的内容能放在前面的?
集群搜索问题如何聚合多个节点或分片的数据生成返回结果在对Mysql进行分库分表的时候 , 经常会遇到一个问题:如果查询的数据分散在多张表中 , 因为涉及到组合多种表的数据 , 将会非常麻烦;对于有些分页场景 , 更是一个灾难 , 所以对Mysql分库分表的时候经常会基于查询维度来尽量避免跨表查询的场景 。 ElasticSearch也是分布式的 , 当数据分散与多个节点或者分片上时 , 他是如何解决数据聚合问题的呢?另外 , 搜索基本都需要排序 , 如何解决排序问题呢?
ES整体流程假设有N个分片 , 数据可能分散在这N个分片上 , ES搜索时 , 整体操作过程是:
  • S1: 客户端将会同时向N个分片发起搜索请求 。
  • S2: 这N个分片基于本分片的内容独立完成搜索 , 然后将符合条件的结果全部返回 。
  • S3: 客户端将返回的结果进行重新排序和排名 , 最后返回给用户 。
【关于ElasticSearch搜索效果的问题分析】有经验的开发很容易看出来 , 这里有两个问题:
  • 数量问题 。 假设每次返回10条记录 , 那么这N个分片独立执行查询以后 , 每个分片最多都会返回10条数据给客户端 , 然后客户端在进行排序返回给用户 。 这个过程中返回的数据量(最大是10*N)会远大于用户请求需要的数据量 。
  • 排名问题 。 计算分值使用的词频和文档频率等信息都是基于自己分片的数据进行的 , 不同分片中这些数据不同 , 直接导致各个分片算出来的分数不具有统一参考性 , 影响排名准确性 。 正确的做法是基于整体的词频、逆向文档频率等信息来算分数 。
查询方式ElasticSearch查询的时候可以指定搜索类型
  • QUERY_AND_FEATCH**
向索引的所有分片(shard)都发出查询请求 , 各分片返回的时候把元素文档(document)和计算后的排名信息一起返回 。 这种搜索方式是最快的 , 只需要去shard查询一次 , 但是各个shard返回的结果的数量之和可能是用户要求的size的n倍 。
  • QUERY_THEN_FETCH
先向所有的shard发出请求 , 各分片只返回排序和排名相关的信息(注意 , 不包括文档document) , 然后按照各分片返回的分数进行重新排序和排名 , 取前size个文档;接着去相关的shard取document 。 这种方式返回的document与用户要求的size是相等的 。
  • DFS_QUERY_AND_FEATCH
在进行真正的查询之前 , 先把各个分片的词频和文档频率收集一下 , 然后进行词搜索的时候 , 各分片依据全局的词频率和文档频率进行搜索和排名 。 接着按照QUERY_AND_FEATCH的方式查询 。
  • DFS_QUERY_THEN_FEATCH
和上面一种方式一样 , 也是先收集词频和文档频率 , 然后再按照QUERY_THEN_FEATC的方式查询 。 这种查询要前后交互三次 , 速度最慢 , 但是排名最准确 。
相关搜索问题ES是如何将相关度高的内容能放在前面的?在原理篇我们知道 , 当将一个文档保存到ElasticSearch会根据分词的结果创建倒排索引 , 这种结构是零散的 , 即每一个Term都会对应Posting List 。 查询的时候也是先经过分词 , 然后根据倒排索引查询 。 这里就有一个问题 , ElasticSearch是如何将匹配度最高的内容放在前面的?如下图所示 , 匹配效果最好的内容放到了返回结果的最前面 。
关于ElasticSearch搜索效果的问题分析文章插图
相关度Lucene 使用布尔模型(Boolean model)查找匹配文档 , 并使用权重来实现相关度搜索