zoukankan      html  css  js  c++  java
  • 【Elasticsearch学习】文档搜索全过程

      在ES执行分布式搜索时,分布式搜索操作需要分散到所有相关分片,若一个索引有3个主分片,每个主分片有一个副本分片,那么搜索请求会在这6个分片中随机选择3个分片,这3个分片有可能是主分片也可能是副本分片,然后收集所有分片的查询结果。所以ES的搜索过程分为两个阶段,Query阶段和Fetch阶段;ES有两种搜索类型:query_then_fetch,dfs_query_then_fetch。

      1.Query阶段

      1)转发请求。在Query阶段客户端向ES节点发送,搜索请求,Coordinate节点接受客户端搜索请求,Coordinate节点负责解析搜索请求,并在索引的所有主副本分片中随机选择分片,并且发送给分片所在的数据节点。

      2)执行查询。接收到查询请求的数据节点执行查询操作,并对查询结果进行排序,每个节点都会根据请求中参数返回from+size个排序后的文档Id和排序值给Coordinate节点。

      2.Fetch阶段

      1)重排序。Coordinate节点收到数据节点返回的数据后,会按照返回的排序值对从所有分片取回的值重新进行排序,最终只选取客户端需要的from+size个文档的Id。

      2)获取文档数据。Coordinate节点根据选取的文档的Id,到相应的分片获取详细的文档数据,最终将查询到的结果返回给客户端。

    查询结果解读:

    {
        "took":3, 查询所用的毫秒数
        "timed_out":false, 是否有分片超时,即是否只返回了部分结果
        "_shards":{
            "total":1, 一共查询了多少分片
            "successful":1, 多少分片成功返回
            "skipped":0,跳过了多少分片
            "failed":0  多少分片查询失败
        },
        "hits":{  
            "total":{ 
                "value":1, 该搜索请求中返回的所有匹配的数量
                "relation":"eq" 文档与搜索值的关系,eq表示相等
            },
            "max_score":8.044733, 返回结果中文档的最大得分
            "hits":[  查询结果的文档数组
                {
                    "_index":"kibana_sample_data_ecommerce", 查询的索引
                    "_type":"_doc",  查询的类型
                    "_id":"4X-j7XEB-r_IFm6PISqV", 返回文档的主键
                    "_score":8.044733,  返回文档的评分
                    "_source":{  文档的原始内容
                        "currency":"EUR",
                        "customer_first_name":"Eddie",
                        "customer_full_name":"Eddie Underwood",
                        "customer_gender":"MALE"
                        ......
                    }
                }
            ]
        }
    }    
    

     Query Then Fetch潜在的问题

    1.深度分页

      ES索引数据分布在多个分片上,在查询时,每个分片都要查询from+size个文档,Coordinate节点会聚合所有的结果,所以Coordinate节点要处理查询分片数*(from+size)个文档记录,对这些记录进行重新排序,需要的size个文档,from+size的值越大占用内存越多,称为深度分页问题,ES默认限制分页的深度不能超过10000条,可通过max_result_window设置。

      深度分页解决办法:

      1)Search After

      可以使用Search After避免深度分页的性能问题,实时获取下一页的文档信息,search_after根据上一页最后一个文档的sort值来查询下一页,并且当索引数据有变化时,也可以同步被查到,是一个实时查询的方法。

      例:http://127.0.0.1:9200/kibana_sample_data_ecommerce/_search

        查询参数:在使用Search_After查询时,第一步查询时需要指定sort字段,并且该sort字段的排序结果是唯一的,建议使用_id来进行sort,可以指定多个sort字段。

    {
      "size": 1,
      "query": {
        "match": {
          "currency": "EUR"
        }
      },
      "sort": [
        {
          "order_id": {
            "order": "asc"
          }
        }
      ]
    }
    

       返回中可以看到第一页查询返回的sort值,查询下一页时使用该sort值进行文档的定位,而后每个查询都会返回一个sort值,供下一页进行定位使用。

    "sort": [
         "550375"
    ]
    

      下一页查询:

    {
      "size": 1,
      "query": {
        "match": {
          "currency": "EUR"
        }
      },
      "search_after": [
        550375
      ],
      "sort": [
        {
          "order_id": {
            "order": "asc"
          }
        }
      ]
    }
    

      Search_After存在的限制:

        a.不能指定from值,即不能想翻到哪一页就直接跳转到那一页,只能一页一页按照顺序翻;

        b.只能往后翻页,不能往前翻页。

      2)Scroll API

      scroll api可以用于从单个搜索请求中检索大量的结果,其原理是建立索引在某个时间点的快照,当快照建立后,之后的每次搜索都会在该快照上进行,对索引的所有新增操作都会被忽略,索引Scroll适合于处理大量数据,但是不能保证数据的实时性。

      POST http://127.0.0.1:9200/kibana_sample_data_ecommerce/_search?scroll=1m

      首次查询时指定scroll=5m,表示当前搜索过期时间为5分钟,即查询结果在搜到下一次请求之前会保存多次时间,scroll的值不需要长到把整个快照的数据都处理完,只需保证下一次搜索请求到来之前能处理完前一批查询结果即可。

    {
        "size": 2,
        "query": {
            "match" : {
                "currency" : "EUR"
            }
        }
    }
    

      返回中可以看到_scroll_id,total.value,scroll_id用于获取下一批的查询结果,total.value表示该查询有总共多少个结果。

    {
       "_scroll_id":"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAABAGUWdks0dUtFMHZTYmE1Rl9ucGp5X0hoUQ==", 
      "took": 1,
        "timed_out": false,
        "_shards": {
            "total": 1,
            "successful": 1,
            "skipped": 0,
            "failed": 0
        },
        "hits": {
            "total": {
                "value": 4675,
                "relation": "eq"
            },
       }
    }

      下一页:

      http://127.0.0.1:9200/_search/scroll

      下一页查询的时候不用指定索引和查询参数,只需要指定scroll时间和上一次请求返回的scroll_id,因为快照已经建好,只需要在快照上往下翻页即可。每次执行该请求都会往下进行翻页,直到查询的结果为空。

    {
      "scroll":"5m",
     "scroll_id":"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAABAGUWdks0dUtFMHZTYmE1Rl9ucGp5X0hoUQ=="
    }
    

      Scroll API存在的限制:当快照建立后,对索引有新的操作时,无法被查询到,所以不适合做实时查询。

    不同查询的使用场景

      一般查询:需要获取顶部的部分文档,查询索引最新的数据。

      全量查询:使用scroll,当需要导出全部数据,且对数据的实时性要求不高时。

      分页查询:使用from+size,当from+size过大时,使用search after。

    2.相关度评分不准问题

      当搜索请求在多个shard进行数据查找时,每个分片都会基于自己分片上的文档数据进行相关度的计算,计算方法为TD/IDF,

      TF:词频,表示词条在一个文档中出现的频率;IDF:逆文档频率,log(全本文档数/词条在所有文档中出现的次数),表示该term在所有文档中出现的频率;如果查询词条在某一个文档中出现的频率(即TF)高,在全部文档中出现的频率低(即IDF)低,则表明该文档的相关性高。

      每个分片计算IDF的时候只会基于自己分片上的数据进行计算,并不会包含其他分片上的数据,所以这样会导致相关性评分不准的情况;特别在文档总数很少情况下,主分片数越多,相关性算分会越不准。

      解决相关度评分不准问题的方法:

      1)合理设置分片数量,保证数据均匀分布。

       当数据量不大时,可以考虑仅设置一个主分数;当数据量较大时,保证文档均匀的分布在各个分片上。ES提供了routing_partition_size参数,routing_partition_size越大,数据的分布越均匀(【Elasticsearch学习】之一图读懂文档索引全过程 中有提及)。

      2)使用dfs_query_then_fetch

      在搜索时,指定搜索的类型search_type=dfs_query_the_fetch,在搜索的时候,每个分片会把每个分片的TF和IDF进行搜集,然后综合所有的数据进行一次完整的相关性评分计算,但是一般不推荐,因为这样会耗费较多的CPU和内存。

     

  • 相关阅读:
    fedora上部署ASP.NET——(卡带式电脑跑.NET WEB服务器)
    SQL Server 请求失败或服务未及时响应。有关详细信息,请参见事件日志或其它适合的错误日志
    8086CPU的出栈(pop)和入栈(push) 都是以字为单位进行的
    FTP 服务搭建后不能访问问题解决
    指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配
    Linux 安装MongoDB 并设置防火墙,使用远程客户端访问
    svn Please execute the 'Cleanup' command. 问题解决
    .net 操作MongoDB 基础
    oracle 使用绑定变量极大的提升性能
    尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将出现此问题。
  • 原文地址:https://www.cnblogs.com/sirhuoshan/p/12865765.html
Copyright © 2011-2022 走看看