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

时间:2017-05-28 07:58:24

标签: performance elasticsearch query-performance nosql

TL; DR

与SQL Server上的相同查询相比,我的ElasticSearch查询需要永远 难道我做错了什么?有没有办法提高我的查询性能?
这只是RDBMS比NoSQL更好的事情之一吗?

前提

我们说我有一家接受订单并提供所需物品的公司。

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

问题

使用ElasticSearch,我必须使用 Terms Aggregation 按订单ID,基数聚合对文档进行分组,以获得唯一的项目数,以及 Average Bucket 聚合以获得每个订单的平均项目数。

我的设置大约需要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 ,这导致我的服务器关闭。
在我的更高的冲压装置上发出相同的请求产生了以下断路器:

[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在这个问题上有几个(目前)未解决的问题:

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

所有这些都促使我使用执行提示&#34; global_ordinals_hash&#34;这使得查询能够成功完成(虽然花了很多时间......)

类似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
   }
}

非常感谢任何帮助:)

1 个答案:

答案 0 :(得分:1)

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

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

我还设法将订单ID转换为整数,这大大提高了性能(尽管SQL Server也提高了性能)。

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

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