Агрегация терминов опирается на внутреннюю структуру данных, известную как глобальные ординалы. Эти структуры хранят статистику для каждого уникального значения данного поля. Эти статистические данные вычисляются, хранятся на уровне осколков и далее объединяются на этапе уменьшения для получения конечного результата.
Глобальные ординалы и поля высокой кардинальности в Elasticsearch
Производительность агрегации терминов по заданному полю может ухудшаться по мере увеличения количества уникальных возможных значений для этого поля (высокая кардинальность), а также потому, что по умолчанию эти статистики лениво вычисляются, то есть их приходится вычислять при первом запуске агрегации после последнего обновления.
Если вы часто индексируете документы, содержащие поля с высокой кардинальностью, и часто выполняете агрегацию терминов по этим полям, ваш кластер может столкнуться с этой проблемой, поскольку глобальные ординалы будут часто пересчитываться.
Elastic предлагает три подхода для решения этой проблемы:
Во-первых, учтите, что глобальные ординалы рассчитываются для каждого осколка, поэтому их нужно пересчитывать только для тех осколков, которые подвергаются изменениям, в остальных случаях можно использовать уже рассчитанные значения. Учитывая это, для индексов, содержащих данные временных рядов, хорошей идеей будет реализовать их как индексы, основанные на времени, чтобы уменьшить количество изменяемых осколков и, в конечном счете, количество глобальных ординалов, которые необходимо будет пересчитать. Поэтому вместо того, чтобы хранить все данные временных рядов в одном огромном индексе, подумайте о том, чтобы разбить его на более мелкие периодические индексы.
Во-вторых, если вы готовы поступиться некоторой нагрузкой на производительность, связанной с временем поиска и временем получения данных, вы можете переключить обработку глобальных ординалов с ленивой на активную. Это означает, что кластер будет обрабатывать их каждый раз, когда обновляются новые сегменты, а не когда выполняется первая агрегация. Вы можете минимизировать влияние на время поступления, если также решите уменьшить интервал обновления.
Вы можете включить стратегию ускоренного вычисления глобальных ординалов для данного поля, установив параметр eager_global_ordinals в мапинге поля, как показано ниже:
1 2 3 4 5 6 7 8 9 | PUT my_index/_mapping/_doc { "properties": { "email": { "type": "keyword", "eager_global_ordinals": true } } } |
Третий подход, который предлагает Elastic (его следует использовать с осторожностью), заключается в том, чтобы вообще не строить глобальные ординалы, а вместо этого заставить кластер вычислять метрики агрегации непосредственно над необработанными значениями поля. Это устраняет проблемы с производительностью при вычислении глобальных ординалов для полей с высокой кардинальностью, однако это вредит последующим агрегациям терминов, поскольку они не получат преимущества от глобальных ординалов. Это также приведет к увеличению использования памяти Elasticsearch, поскольку ему придется строить и хранить карту с каждым найденным уникальным значением.
В таком случае Elastic рекомендует использовать этот подход только в тех случаях, когда агрегация будет применяться к небольшому количеству документов, обычно путем их фильтрации.
Если вы решите попробовать, единственное, что вам нужно сделать, это задать параметр "execution_hint": "map" в вашем агрегаторе терминов, например, так:
1 2 3 4 5 6 7 8 | "aggregations": { "emails": { "terms": { "field": "email", "execution_hint": "map" } } } |
Выясните, не сталкиваетесь ли вы с проблемой чрезмерного вычисления глобальных ординалов
Вы можете выяснить, есть ли у вас проблемы с производительностью при вычислении глобальных ординалов, временно включив ведение журнала на уровне TRACE. Выполните следующую команду, чтобы изменить соответствующую настройку кластера переходным способом, то есть она не будет сохранена в состоянии кластера:
1 2 3 4 5 6 | PUT _cluster/settings { "transient": { "logger.org.elasticsearch.index.fielddata": "TRACE" } } |
После этого найдите в файле elasticsearch.log записи, подобные следующей:
1 | global-ordinals [<email>][1014089] took [592.3ms] |
Это даст вам представление о том, сколько времени занимает вычисление глобальных ординалов и влияет ли это на производительность ваших агрегаций. После завершения исследования не забудьте вернуть настройки ведения журнала в менее подробный режим.
Вы также можете проверить размер структуры данных глобальных ординалов, вызвав API _stats и посмотрев на значение memory_size_in_bytes:
1 | GET <index_name>/_stats/fielddata?fielddata_fields=<field_name> |
Только не забудьте сбросить настройки переходного процесса после завершения отладки. Для этого присвойте ему null значение:
1 2 3 4 5 6 | PUT _cluster/settings { "transient": { "logger.org.elasticsearch.index.fielddata": null } } |
Профилирование
Elasticsearch предоставляет API Profile в качестве инструмента отладки, который позволяет получить подробную информацию о выполнении запроса, включая агрегации.
При профилировании запроса следует помнить, что:
- Он увеличивает общее время выполнения, поскольку ему необходимо отслеживать и собирать метрики о каждой фазе выполнения запроса.
- В настоящее время он не измеряет фазу выборки поиска и сетевые накладные расходы.
- Профилирование фазы уменьшения агрегации в настоящее время недоступно.
- Elastic считает этот API экспериментальным и находящимся в стадии разработки, так как они используют части Lucene, которые не были предназначены для профилирования, поэтому могут возникать странные поведения/числа. Будьте внимательны.