performance - 具有高基数字段的 ElasticSearch 术语和基数性能

标签 performance elasticsearch query-performance nosql

TL;博士

与 SQL Server 上的相同查询相比,我的 ElasticSearch 查询需要很长时间。
难道我做错了什么?有什么方法可以提高我的查询性能?
它只是 RDBMS 比 NoSQL 做得更好的事情之一吗?

前提

假设我有一家接受订单并交付所需元素的企业。

  • 我想知道每个订单的平均独特商品数量。
  • 我的订单数据按订购的每件商品排列 - 每个订单都有一条或多条记录,其中包含订单 ID、商品 ID 等。
  • 我有一个用于开发目的的单节点设置
  • 无论我有 4 GB 堆空间(在 12 GB 机器上)还是 16 GB 堆空间(在 32 GB 机器上),结果(性能方面)都是相同的
  • 该索引有数十亿条记录,但查询将其过滤为大约 300,000 条记录
  • 订单和商品 ID 属于关键字类型(本质上是文本),我无法更改它。
  • 在这种特殊情况下,平均独特商品数为 1.65 - 许多订单仅包含一件独特商品,其他订单包含 2 件,少数包含多达 25 件独特商品。

  • 问题

    使用 ElasticSearch,我将不得不使用术语聚合按订单 ID 对文档进行分组,使用基数聚合来获得唯一项目数,并使用平均桶聚合来获得每个订单的平均项目数。

    这在我的两个设置中都需要大约 23 秒。在 SQL Server 上使用相同的数据集进行相同的查询不到 2 秒。

    附加信息

    Elasticsearch 查询
    {
       "size":0,
       "query":{
          "bool":{
             "filter":[
                {
                   ...
                }
             ]
          }
       },
       "aggs":{
          "OrdersBucket":{
             "terms":{
                "field":"orderID",
                "execution_hint":"global_ordinals_hash",
                "size":10000000
             },
             "aggs":{
                "UniqueItems":{
                   "cardinality":{
                      "field":"itemID"
                   }
                }
             }
          },
          "AverageItemCount":{
             "avg_bucket":{
                "buckets_path":"OrdersBucket>UniqueItems"
             }
          }
       }
    }
    

    起初,我的查询生成了 OutOfMemoryException,导致我的服务器停机。
    在我更高的 ram 设置上发出相同的请求会产生以下断路器:

    [request] Data too large, data for [<reused_arrays>] would be
    [14383258184/13.3gb], which is larger than the limit of
    [10287002419/9.5gb]
    


    ElasticSearch github 在这个问题上有几个(当前) Unresolved 问题:

    Cardinality aggregation should not reserve a fixed amount of memory per bucket #15892

    global_ordinals execution mode for the terms aggregation has an adversarially impact on children aggregations that expect dense buckets #24788

    Heap Explosion on even small cardinality queries in ES 5.3.1 / Kibana 5.3.1 #24359

    所有这些都导致我使用执行提示“global_ordinals_hash”,它允许查询成功完成(尽管需要时间..)

    类比 SQL 查询
    SELECT AVG(CAST(uniqueCount.amount AS FLOAT)) FROM 
    (   SELECT o.OrderID, COUNT(DISTINCT o.ItemID) AS amount 
        FROM Orders o
        WHERE ...
        GROUP BY o.OrderID 
    ) uniqueCount
    

    正如我所说,这非常非常快。

    orderID 字段映射
    {
       "orderID":{
          "full_name":"orderID",
          "mapping":{
             "orderID":{
                "type":"keyword",
                "boost":1,
                "index":true,
                "store":false,
                "doc_values":true,
                "term_vector":"no",
                "norms":false,
                "index_options":"docs",
                "eager_global_ordinals":true,
                "similarity":"BM25",
                "fields":{
                   "autocomplete":{
                      "type":"text",
                      "boost":1,
                      "index":true,
                      "store":false,
                      "doc_values":false,
                      "term_vector":"no",
                      "norms":true,
                      "index_options":"positions",
                      "eager_global_ordinals":false,
                      "similarity":"BM25",
                      "analyzer":"autocomplete",
                      "search_analyzer":"standard",
                      "search_quote_analyzer":"standard",
                      "include_in_all":true,
                      "position_increment_gap":-1,
                      "fielddata":false
                   }
                },
                "null_value":null,
                "include_in_all":true,
                "ignore_above":2147483647,
                "normalizer":null
             }
          }
       }
    }
    

    我设置了 eager_global_ordinals 试图提高性能,但无济于事。

    样本文件
    {
                "_index": "81cec0acbca6423aa3c2feed5dbccd98",
                "_type": "order",
                "_id": "AVwpLZ7GK9DJVcpvrzss",
                "_score": 0,
                "_source": {
            ...
                   "orderID": "904044A",
                   "itemID": "23KN",
            ...
                }
    }
    

    为了简洁和不公开的内容,删除了不相关的字段

    样本输出
    {
       "OrdersBucket":{
          "doc_count_error_upper_bound":0,
          "sum_other_doc_count":0,
          "buckets":[
             {
                "key":"910117A",
                "doc_count":16,
                "UniqueItems":{
                   "value":16
                }
             },
             {
                "key":"910966A",
                "doc_count":16,
                "UniqueItems":{
                   "value":16
                }
             },
            ...
             {
                "key":"912815A",
                "doc_count":1,
                "UniqueItems":{
                   "value":1
                }
             },
             {
                "key":"912816A",
                "doc_count":1,
                "UniqueItems":{
                   "value":1
                }
             }
          ]
       },
       "AverageItemCount":{
          "value":1.3975020363833832
       }
    }
    

    任何帮助将不胜感激:)

    最佳答案

    显然 SQL Server 在缓存这些结果方面做得很好。
    进一步调查显示,初始查询与 ElasticSearch 所用的时间相同。

    我将研究为什么这些结果没有通过 ElasticSearch 正确缓存。

    我还设法将订单 ID 转换为整数,这极大地提高了性能(尽管与 SQL Server 的性能提升相同)。

    另外,as advised by Mark Harwood on the Elastic Forum ,在基数聚合上指定precision_threshold 大大降低了内存消耗!

    所以答案是,对于这种特定类型的查询,ES 的性能至少与 SQL Server 一样好。

    关于performance - 具有高基数字段的 ElasticSearch 术语和基数性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44225038/

    相关文章:

    performance - 该日期列上的非唯一索引会减慢插入具有相同日期值的 50 万个条目的速度吗?

    performance - 只读对 SQL Server 没有影响吗?

    elasticsearch - Elasticsearch长短语搜索

    elasticsearch - 如何在Elasticsearch中搜索字段数组

    postgresql - 在不损失性能的情况下同时执行多个功能

    java - 在Android中安排JobIntentService

    java - 如何针对 vanilla 基准调整 Java GC?

    apache-spark - Spark + Elastic搜索写入性能问题

    重复时 MySQL 查询运行速度变慢

    java - 在 Hibernate 中通过 id 有效加载多个实体