• elasticsearch查询之三种fetch id的方案分析


    一、使用场景介绍

    elasticsearch除了普通的全文检索之外,在很多的业务场景中都有使用,各个业务模块根据自己业务特色设置查询条件,通过elasticsearch执行并返回所有命中的记录的id;如果命中的记录数达到数万级别的话,查询性能会有明显的下降,尤其是命中超大型的document的时候;

    获取记录的id目前可以使用的有三种方式;

    通过_source:["id"]

    设置_source:false,通过es返回的元数据_id分离出device的id;

    使用store=true来单独的存储device id,查询的时候使用stored_fields= ['id'];

    二、store映射参数

    默认情况下,字段值会被索引以使其可搜索,但不会存储它们。这意味着可以查询该字段,但不能检索原始字段值。

    通常这并不重要。该字段值已经是_source字段的一部分,该字段是默认存储的。如果您只想检索单个字段或几个字段的值,而不是整个_source,那么可以通过_source过滤来实现。

    在某些情况下,存储字段是有意义的。例如,如果你有一个文档,一个标题,一个日期,和一个非常大的内容字段,你可能想只检索标题和日期,而不必从一个大的_source字段提取这些字段:

    设置对应字段的store参数为true,并创建mapping;

    PUT my_store_test
    {
      "mappings": {
        "_doc": {
          "properties": {
            "title": {
              "type": "text",
              "store": true 
            },
            "date": {
              "type": "date",
              "store": true 
            },
            "content": {
              "type": "text"
            }
          }
        }
      }
    }
    
    
    
    {
      "acknowledged" : true,
      "shards_acknowledged" : true,
      "index" : "my_store_test"
    }
    
    

    put一个document进行索引

    PUT my_store_test/_doc/1
    {
      "title":   "Some short title",
      "date":    "2015-01-01",
      "content": "A very long content field..."
    }
    
    {
      "_index" : "my_store_test",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 1,
      "result" : "created",
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 0,
      "_primary_term" : 1
    }
    
    

    通过在查询语句中设置stored_fields来筛选要返回的字段,elasticsearch返回的fields字段包含对应的字段值;

    GET my_store_test/_search
    {
      "stored_fields": [ "title", "date" ] 
    }
    
    
    {
      "took" : 2,
      "timed_out" : false,
      "_shards" : {
        "total" : 5,
        "successful" : 5,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : 1,
        "max_score" : 1.0,
        "hits" : [
          {
            "_index" : "my_store_test",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 1.0,
            "fields" : {
              "date" : [
                "2015-01-01T00:00:00.000Z"
              ],
              "title" : [
                "Some short title"
              ]
            }
          }
        ]
      }
    }
    

    三、测试情况

    我们测试使用my_store_index,里边包含50W的document,还有一些特别大的document;

    我们fetch_ids_query进行测试

    默认情况下通过elasticsearch查询返回的_source字段获取记录的id字段;

    通过take_from__id控制从elasticsearch查询返回的元数据_id解析出记录id;

    通过task_stored_fields控制从elasticsearch查询返回的fields获取记录的id;

    from elasticsearch import Elasticsearch
    from elasticsearch_dsl import Search, Q
    import time
    
    
    def fetch_ids_query(client, take_from__id = False, task_stored_fields = False):
        start = time.time()
        s = Search(using=client, index="my_store_index")
        s = s.params(http_auth=["test", "test"], request_timeout=50);
        q = Q('bool',
              must_not=[Q('match_phrase_prefix', name='us')]
              )
        s = s.query(q)
    
        s = s.source(False) if take_from__id else s.source(['id'])
        if task_stored_fields:
            s = s.extra(stored_fields= ['id'])
            s = s.source(False)
    
        s = s[0:40000]
        response = s.execute()
    
        print(f'hit total {response.hits.total}')
        print(f'fetch total {len(response.hits.hits)}')
        
    
        ids = []
        if take_from__id:
            for hit in response.hits.hits:
                id = hit['_id'][37:]
                ids.append(id)
        elif task_stored_fields:
            for hit in response.hits.hits:
                id = hit.fields['id'][0]
                ids.append(id)
        else:
            for hit in response.hits.hits:
                id = hit._source['id']
                ids.append(id)
    
        end = time.time()
        print(f"all execute time {end - start}s")
        
    
    client = Elasticsearch(hosts=['http://127.0.0.1:9200'], http_auth=["test", "test"])
    
    print('fetch id from source')
    fetch_ids_query(client);
    print()
    print('fetch id from _id and set source = false')
    fetch_ids_query(client, True);
    print()
    print('fetch id from stored id and set source = false')
    fetch_ids_query(client, False, True);
    

    四、结果分析

    经测试在命中484970,fetch 40000条记录的前提下,后两种方式的执行时间更短,但是通过元数据解析_id会更加友好,不仅节省存储空间,而且查询的时候避免了内存和CPU的震荡;

    fetch id from source
    hit total 484970
    fetch total 40000
    all execute time 28.691869497299194s
    
    fetch id from _id and set source = false
    hit total 484970
    fetch total 40000
    all execute time 11.315539121627808s
    
    fetch id from stored id and set source = false
    hit total 484970
    fetch total 40000
    all execute time 13.930094957351685s
    
  • 相关阅读:
    04: vue生命周期和实例属性和方法
    03: vuejs 事件、模板、过滤器
    (打补丁 )patch
    zabbix安装
    zabbix简介
    linux 虚拟网络模型介绍
    虚拟化
    虚拟化分类(半虚拟化和全虚拟化)
    playbook详解—YAML格式的文本
    ansible的介绍和一些基本模块介绍
  • 原文地址:https://www.cnblogs.com/wufengtinghai/p/15911856.html
Copyright © 2020-2023  润新知