То, что вы видите, - это нормальное поведение для агрегаций Elasticsearch (на самом деле, довольно хорошая производительность, если у вас есть 1 миллиард документов).
Существует несколько вариантов, которые вы можете рассмотреть: использование пакета filter
агрегаций, повторная индексация с подмножеством документов, загрузка данных из Elasticsearch и вычисление сопутствующих явлений. в автономном режиме.
Но, вероятно, стоит попытаться отправить эти 10К-запросы и посмотреть, сработает ли встроенное кэширование Elasticsearch.
Позвольте мне объяснить более подробно каждый из этих вариантов.
Использование filter
агрегация
Во-первых, давайте обрисуем, что мы делаем в исходном запросе ES:
- фильтровать документы с помощью
create_time
в определенном временном окне;
- фильтровать документы, содержащие нужный тег
dynamic_single_tag
;
- также фильтрует документы, у которых есть хотя бы один тег из списка
fixed_set_tags_list
;
- посчитайте, сколько таких документов существует за каждый месяц в определенный период времени.
Производительность - это проблема, потому что у нас есть 10 000 тегов для таких запросов.
Здесь мы можем переместить filter
на dynamic_single_tag
из запроса в агрегаты:
POST myindex/_doc/_search
{
"size": 0,
"query": {
"bool": {
"filter": [
{ "terms": { ... } }
]
}
},
"aggs": {
"by tag C": {
"filter": {
"term": {
"tags": "C" <== here's the filter
}
},
"aggs": {
"by month": {
"date_histogram": {
"field": "created_time",
"interval": "month",
"min_doc_count": 0,
"extended_bounds": {
"min": "2019-01-01",
"max": "2019-02-01"
}
}
}
}
}
}
}
Результат будет выглядеть примерно так:
"aggregations" : {
"by tag C" : {
"doc_count" : 2,
"by month" : {
"buckets" : [
{
"key_as_string" : "2019-01-01T00:00:00.000Z",
"key" : 1546300800000,
"doc_count" : 2
},
{
"key_as_string" : "2019-02-01T00:00:00.000Z",
"key" : 1548979200000,
"doc_count" : 0
}
]
}
}
Теперь, если вы спрашиваете, как это может помочь производительности, вот хитрость: добавить больше таких filter
агрегатов для каждого тега: "by tag D"
, "by tag E"
и т. Д.
Улучшение будет достигнуто за счет выполнения «пакетных» запросов, , объединяющих много начальных запросов в один . Возможно, нецелесообразно помещать все 10 тыс. Из них в один запрос, но даже партии по 100 тегов на запрос могут изменить правила игры.
(Примечание: примерно такое же поведение можно достичь с помощью агрегации terms
с параметром фильтра include
.)
Этот метод, конечно, требует грязных рук и написания более сложного запроса, но он пригодится, если вам понадобится запускать такие запросы в случайное время с подготовкой 0.
переиндексировать документы
Идея второго метода состоит в том, чтобы заранее сократить набор документов с помощью reindex API . reindex
запрос может выглядеть так:
POST _reindex
{
"source": {
"index": "myindex",
"type": "_doc",
"query": {
"bool": {
"filter": [
{
"range": {
"created_time": {
"gte": "fixed_start_time",
"lte": "fixed_end_time",
"format": "yyyy-MM-dd-HH"
}
}
},
{
"terms": {
"tags": {
"index": "fixed_set_tags_list",
"id": 2,
"type": "twitter",
"path": "tag_list"
}
}
}
]
}
}
},
"dest": {
"index": "myindex_reduced"
}
}
Этот запрос создаст новый индекс myindex_reduced
, содержащий только те элементы, которые удовлетворяют первым 2 пунктам фильтрации.
На этом этапе исходный запрос может быть выполнен без этих двух предложений.
Ускорение в этом случае будет связано с ограничением количества документов: чем оно меньше, тем больше выигрыш. Так что, если fixed_set_tags_list
оставляет вам небольшую часть в 1 миллиард, это вариант, который вы обязательно можете попробовать.
Загрузка данных и обработка вне Elasticsearch
Если честно, этот вариант использования больше похож на работу для панд . Если вы используете аналитику данных, я бы предложил использовать scroll API для извлечения данных на диск, а затем обработать их произвольным скриптом.
В python это может быть так же просто, как использовать .scan()
вспомогательный метод из библиотеки elasticsearch
.
Почему бы не попробовать метод грубой силы?
Elasticsearch уже попытается помочь вам с вашим запросом по request cache
. Он применяется только к чисто агрегационным запросам (size: 0
), поэтому должен работать в вашем случае.
Но это не так, потому что содержимое запроса всегда будет другим (весь JSON запроса используется в качестве ключа кэширования , и у нас есть новый тег в каждом запросе). Начнет играть другой уровень кэширования.
Elasticsearch в значительной степени опирается на кэш файловой системы , что означает, что под капотом наиболее часто используемые блоки файловой системы будут кэшироваться (практически загружаться в ОЗУ). Для конечного пользователя это означает, что «разогрев» будет происходить медленно и с объемом подобных запросов.
В вашем случае агрегация и фильтрация будут выполняться в 2 полях: create_time
и tags
.Это означает, что после выполнения, возможно, 10 или 100 запросов с разными тегами, время отклика упадет с 1,5 с до чего-то более терпимого.
Чтобы продемонстрировать свою точку зрения, вот график Vegeta из моего исследования производительности Elasticsearch по тому же запросу с большими агрегатами, отправленными с фиксированным RPS:
![Filesystem cache kicks in](https://i.stack.imgur.com/X1v6f.png)
Как видите, изначально запрос принимал ~10 секунд, а после 100 запросов он уменьшился до блестящих 200 мс.
Я бы определенно предложил попробовать этот подход "грубой силы", потому что, если он работает, он хорош, если нет - он ничего не стоит.
Надеюсь, это поможет!