Агрегация терминов опирается на внутреннюю структуру данных, известную как глобальные ординалы. Эти структуры хранят статистику для каждого уникального значения данного поля. Эти статистические данные вычисляются, хранятся на уровне шарда и затем объединяются на этапе уменьшения для получения окончательного результата.
Глобальные ординалы в OpenSearch
Производительность агрегирования терминов по заданному полю может ухудшаться по мере увеличения количества возможных уникальных значений для этого поля (высокая кардинальность), а также потому, что по умолчанию эти статистики вычисляются лениво, то есть их приходится вычислять при первом запуске агрегирования после последнего обновления.
Если вы часто индексируете документы, содержащие поля с высокой кардинальностью, и часто выполняете агрегацию терминов по этим полям, ваш кластер может столкнуться с этой проблемой, поскольку глобальные ординалы будут часто пересчитываться.
OpenSearch предлагает три подхода для решения этой проблемы:
1. Во-первых, учтите, что глобальные ординалы рассчитываются для каждого шарда, поэтому их нужно пересчитывать только для тех шардов, которые подвергаются изменениям, в остальных случаях можно использовать уже рассчитанные значения. Учитывая это, для индексов, содержащих данные временных рядов, целесообразно реализовать их как индексы, основанные на времени, чтобы уменьшить количество изменяемых шардов и, в конечном счете, количество глобальных ординалов, которые необходимо будет пересчитать. Поэтому вместо того, чтобы хранить все данные временных рядов в одном огромном индексе, подумайте о том, чтобы разбить его на более мелкие периодические индексы.
2. Во-вторых, если вы готовы поступиться некоторой нагрузкой на производительность, связанной с временем поиска и временем получения данных, вы можете переключить обработку глобальных ординалов с ленивой на активную. Это означает, что кластер будет обрабатывать их каждый раз, когда обновляются новые сегменты, а не когда выполняется первая агрегация. Вы можете минимизировать влияние на время поступления, если также решите уменьшить интервал обновления.
Вы можете включить стратегию ускоренного вычисления глобальных ординалов для данного поля, установив параметр 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 } } } |
3. Третий предложенный подход (который следует использовать с осторожностью) заключается в том, чтобы вообще не строить глобальные ординалы, а вместо этого заставить кластер вычислять метрики агрегирования непосредственно над необработанными значениями поля. Это устраняет проблемы с производительностью при вычислении глобальных ординалов для полей с высокой кардинальностью, но наносит ущерб последующим агрегациям терминов, поскольку они не получат преимущества от глобальных ординалов. Это также приведет к увеличению использования памяти OpenSearch, поскольку ему придется строить и хранить карту с каждым найденным уникальным значением.
Этот подход следует использовать только в тех случаях, когда агрегация будет применяться к небольшому количеству документов, обычно путем фильтрации документов.
Если вы решите попробовать, единственное, что вам нужно сделать, это задать параметр "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.opensearch.index.fielddata": "TRACE" } } |
После этого найдите в файле opensearch.log записи, подобные следующим:
1 | global-ordinals [<email>][1014087] took [592.3ms] |
Это даст вам представление о том, сколько времени занимает вычисление глобальных ординалов и влияет ли это на производительность ваших агрегатов. После завершения исследования не забудьте вернуть настройки протоколирования в менее подробный режим.
Вы также можете проверить размер структуры данных глобальных ординалов, вызвав API _stats и посмотрев на значение memory_size_in_bytes:
1 | GET <index_name>/_stats/fielddata?fielddata_fields=<field_name> |
Только не забудьте сбросить настройки переходного процесса после завершения отладки. Для этого присвойте ему нулевое значение:
1 2 3 4 5 6 | PUT _cluster/settings { "transient": { "logger.org.opensearch.index.fielddata": null } } |
Профилирование
OpenSearch предоставляет API Profile в качестве инструмента отладки, который позволяет получить подробную информацию о выполнении запроса, включая агрегации.
При профилировании запроса следует помнить, что:
- Это влечет за собой незначительные накладные расходы на общее время выполнения, так как необходимо отслеживать и собирать метрики о каждой фазе выполнения запроса.
- В настоящее время он не измеряет фазу поиска и сетевые накладные расходы.
- Профилирование фазы уменьшения агрегации в настоящее время недоступно.
- OpenSearch считает этот API экспериментальным и все еще находящимся в стадии разработки, так как они используют части Lucene, которые не были предназначены для профилирования, поэтому могут возникать странные поведения/числа. Будьте внимательны.