Terms aggregation(实现分层分面)查询性能慢
Terms aggregation (to achieve hierarchical faceting) query performance slow
我正在弹性搜索中索引指标名称。指标名称的格式为 foo.bar.baz.aux
。这是我使用的索引。
{
"index": {
"analysis": {
"analyzer": {
"prefix-test-analyzer": {
"filter": "dotted",
"tokenizer": "prefix-test-tokenizer",
"type": "custom"
}
},
"filter": {
"dotted": {
"patterns": [
"([^.]+)"
],
"type": "pattern_capture"
}
},
"tokenizer": {
"prefix-test-tokenizer": {
"delimiter": ".",
"type": "path_hierarchy"
}
}
}
}
}
{
"metrics": {
"_routing": {
"required": true
},
"properties": {
"tenantId": {
"type": "string",
"index": "not_analyzed"
},
"unit": {
"type": "string",
"index": "not_analyzed"
},
"metric_name": {
"index_analyzer": "prefix-test-analyzer",
"search_analyzer": "keyword",
"type": "string"
}
}
}
}
以上索引为指标名称创建了以下术语foo.bar.baz
foo
bar
baz
foo.bar
foo.bar.baz
如果我有一堆指标,如下所示
a.b.c.d.e
a.b.c.d
a.b.m.n
x.y.z
我必须编写一个查询来获取第 n 级令牌。在上面的例子中
for level = 0, I should get [a, x]
for level = 1, with 'a' as first token I should get [b]
with 'x' as first token I should get [y]
for level = 2, with 'a.b' as first token I should get [c, m]
除了写术语聚合,我想不出任何其他方法。要找出 a.b
的 2 级标记,这是我提出的查询。
time curl -XGET http://localhost:9200/metrics_alias/metrics/_search\?pretty\&routing\=12345 -d '{
"size": 0,
"query": {
"term": {
"tenantId": "12345"
}
},
"aggs": {
"metric_name_tokens": {
"terms": {
"field" : "metric_name",
"include": "a[.]b[.][^.]*",
"execution_hint": "map",
"size": 0
}
}
}
}'
这将导致以下桶。我解析输出并从那里获取 [c, m]。
"buckets" : [ {
"key" : "a.b.c",
"doc_count" : 2
}, {
"key" : "a.b.m",
"doc_count" : 1
} ]
到目前为止一切顺利。该查询适用于大多数租户(请注意上面的 tenantId
term
查询)。对于某些拥有大量数据(大约 100 万)的租户,性能确实很慢。我猜所有术语聚合都需要时间。
我想知道术语聚合是否是此类数据的正确选择,并且还在寻找其他可能的查询类型。
一些建议:
- "mirror" 查询部分中聚合级别的过滤器也是如此。因此,对于
a.b.
匹配,使用以下作为查询并保留相同的 aggs 部分:
"bool": {
"must": [
{
"term": {
"tenantId": 123
}
},
{
"prefix": {
"metric_name": {
"value": "a.b."
}
}
}
]
}
甚至使用 regexp
和聚合部分中相同的正则表达式。这样,聚合将不得不评估更少的桶,因为到达聚合部分的文档将更少。
你提到 regexp
对你来说效果更好,我最初的猜测是 prefix
会更好。
- 将
"size": 0
从聚合更改为 "size": 100
。测试后你提到这没有任何区别
- 删除
"execution_hint": "map"
并让 Elasticsearch 使用默认值。测试后,您提到默认 execution_hint
的性能要差得多。
- 我唯一能想到的另一件事是通过在索引时移动它来减轻搜索时的压力。我的意思是:在索引时,在您自己的应用程序中或您使用的任何索引方法中,以编程方式拆分要索引的文本(不是 ES 这样做)并在单独的字段中索引层次结构中的每个元素。例如
field2
中的a.b
、field3
中的a.b.c
等。这适用于同一份文件。然后,在搜索时,您会根据搜索文本查看特定字段。但是,整个想法需要在 ES 之外进行一些额外的工作。
在以上所有建议中,第一个影响最大:查询响应时间从 23 秒缩短到 11 秒。
我正在弹性搜索中索引指标名称。指标名称的格式为 foo.bar.baz.aux
。这是我使用的索引。
{
"index": {
"analysis": {
"analyzer": {
"prefix-test-analyzer": {
"filter": "dotted",
"tokenizer": "prefix-test-tokenizer",
"type": "custom"
}
},
"filter": {
"dotted": {
"patterns": [
"([^.]+)"
],
"type": "pattern_capture"
}
},
"tokenizer": {
"prefix-test-tokenizer": {
"delimiter": ".",
"type": "path_hierarchy"
}
}
}
}
}
{
"metrics": {
"_routing": {
"required": true
},
"properties": {
"tenantId": {
"type": "string",
"index": "not_analyzed"
},
"unit": {
"type": "string",
"index": "not_analyzed"
},
"metric_name": {
"index_analyzer": "prefix-test-analyzer",
"search_analyzer": "keyword",
"type": "string"
}
}
}
}
以上索引为指标名称创建了以下术语foo.bar.baz
foo
bar
baz
foo.bar
foo.bar.baz
如果我有一堆指标,如下所示
a.b.c.d.e
a.b.c.d
a.b.m.n
x.y.z
我必须编写一个查询来获取第 n 级令牌。在上面的例子中
for level = 0, I should get [a, x]
for level = 1, with 'a' as first token I should get [b]
with 'x' as first token I should get [y]
for level = 2, with 'a.b' as first token I should get [c, m]
除了写术语聚合,我想不出任何其他方法。要找出 a.b
的 2 级标记,这是我提出的查询。
time curl -XGET http://localhost:9200/metrics_alias/metrics/_search\?pretty\&routing\=12345 -d '{
"size": 0,
"query": {
"term": {
"tenantId": "12345"
}
},
"aggs": {
"metric_name_tokens": {
"terms": {
"field" : "metric_name",
"include": "a[.]b[.][^.]*",
"execution_hint": "map",
"size": 0
}
}
}
}'
这将导致以下桶。我解析输出并从那里获取 [c, m]。
"buckets" : [ {
"key" : "a.b.c",
"doc_count" : 2
}, {
"key" : "a.b.m",
"doc_count" : 1
} ]
到目前为止一切顺利。该查询适用于大多数租户(请注意上面的 tenantId
term
查询)。对于某些拥有大量数据(大约 100 万)的租户,性能确实很慢。我猜所有术语聚合都需要时间。
我想知道术语聚合是否是此类数据的正确选择,并且还在寻找其他可能的查询类型。
一些建议:
- "mirror" 查询部分中聚合级别的过滤器也是如此。因此,对于
a.b.
匹配,使用以下作为查询并保留相同的 aggs 部分:
"bool": {
"must": [
{
"term": {
"tenantId": 123
}
},
{
"prefix": {
"metric_name": {
"value": "a.b."
}
}
}
]
}
甚至使用 regexp
和聚合部分中相同的正则表达式。这样,聚合将不得不评估更少的桶,因为到达聚合部分的文档将更少。
你提到 regexp
对你来说效果更好,我最初的猜测是 prefix
会更好。
- 将
"size": 0
从聚合更改为"size": 100
。测试后你提到这没有任何区别 - 删除
"execution_hint": "map"
并让 Elasticsearch 使用默认值。测试后,您提到默认execution_hint
的性能要差得多。 - 我唯一能想到的另一件事是通过在索引时移动它来减轻搜索时的压力。我的意思是:在索引时,在您自己的应用程序中或您使用的任何索引方法中,以编程方式拆分要索引的文本(不是 ES 这样做)并在单独的字段中索引层次结构中的每个元素。例如
field2
中的a.b
、field3
中的a.b.c
等。这适用于同一份文件。然后,在搜索时,您会根据搜索文本查看特定字段。但是,整个想法需要在 ES 之外进行一些额外的工作。
在以上所有建议中,第一个影响最大:查询响应时间从 23 秒缩短到 11 秒。