Elasticsearch

Как выбрать правильное количество и объём шардов индекса Elasticsearch

"Залог высокой производительности Elasticsearch —корректная настройка количества и объема шардов."
Александр Романюк
автор, инженер-проектировщик систем мониторинга
Индекс Elasticsearch состоит из одного или нескольких основных шардов, которые могут быть реплицированы для минимизации последствий аппаратных сбоев. Начиная с Elasticsearch 7, значение по умолчанию для количества первичных шардов на индекс — 1. В более ранних версиях по умолчанию было 5 первичных шардов. А вот количество реплик по умолчанию как было так и осталось равно 1. Как это ни странно, но некоторые заказчики, которых мне приходилось консультировать, оставляли эти значения по умолчанию. Если ваша цель выжать максимум производительности от железа, дефолтные значения стоит изменить.

У каждого индекса и сегмента есть накладные расходы, и, если данные будут распределены на слишком большое количество шардов, накладные расходы начнут оказывать значительное влияние на производительность. Кластер с избыточным количеством индексов или шардов будет страдать от овершардинга. Кластер с овершардингом будет менее эффективен при поиске, а в крайних случаях может работать нестабильно. В этой статье поговорим о том, как избежать всех этих ужасных последствий и возможностях кастомизации конфигурации индексов, чтобы у вашего кластера Elasticsearch было здоровое пищеварение.

Поиск оптимального количества шардов и их размера зависит от набора факторов. Эти факторы включают в себя: объем данных, формат использования, формат запросов, сложность документов и SLA. Может что-то ещё, но это не точно. Существует два основных подхода к поиску правильного размера сегмента:

  1. Бенчмаркинг — проведение экспериментов
  2. Общие рекомендации – следование общепринятым методам
Прежде чем мы углубимся в подходы и размеры, давайте-ка быстренько рассмотрим, для чего используются шарды в Elasticsearch и почему важно настроить их правильное количество. Обращаю ваше драгоценное внимание, что в этой статье основное внимание будет уделено первичным шардам. Как обычно поговаривают про Elastic:
How many shards should I have?
Well, it depends (evil giggles)...
Какая там польза от нескольких шардов при индексации
А пользы очень даже много. Шарды используются для распараллеливания работы над индексом. Когда вы отправляете запрос на индексацию списка документов, они будут разделены между всеми доступными первичными шардами.

Таким образом, если у вас есть 5 основных шардов и вы отправляете запрос со 100 документами, каждый шард проиндексирует по 20 документов. Как только все документы проиндексированы, ответ отправляется обратно клиенту. Затем может быть проиндексирован следующий пакет документов. Индексирование происходит быстрее, когда больше шардов, так как каждый документ хранится в единственном экземпляре на шарде. Если нужно быстрое получение данных, у вас должен быть как минимум один шард на каждую ноду кластера Elasticsearch. Ну, или можно иметь число шардов, которое без остатка делится на количество нод. Например, если есть 3 ноды в кластере, у вас должно быть 3, 6 или 9 шардов. Это важно. Если есть 3 ноды и вы решите использовать 4 основных шарда, то процесс индексации будет медленнее, чем при использовании 3 сегментов, потому что 1 нода (она же сервер) будет выполнять двойную работу, а две другие будут простаивать.
Эмпирическое правило для быстрого индексирования: вы должны выбрать как минимум столько первичных шардов, сколько у вас есть нод в кластере, при условии, что количество нод данных соответствует вашим требованиям.
Какая там польза от нескольких шардов при поиске
Как работает распараллеливание для поиска? Каждый шард содержит только часть документов, это означает, что каждый сегмент содержит только частичный индекс. Можно думать об этом как о книге с несколькими томами — в каждом томе есть свой частичный индекс.

Когда вы отправляете поисковый запрос, узел, получивший запрос, действует как координирующая нода, которая затем ищет, какие шарды принадлежат этому индексу в соответствии с состоянием кластера. Затем координирующая нода отправит этот запрос всем обнаруженным шардам.

После этого каждый шард локально вычисляет список результатов с идентификаторами документов и оценками («фаза запроса»). Эти частичные результаты отправляются обратно на координирующую ноду, которая объединяет их в общий результат. Затем начинается вторая фаза («фаза выборки»), когда извлекается список документов. Если вы выберете размер по умолчанию 10, будет получено 10 документов. Фаза выборки также должна быть выполнена для всех шардов. Когда все документы возвращены, может быть выполнена некая постобработка, например, выделение (highlighting), чтобы затем весь ответ отправить обратно клиенту. Очевидно, что распараллеливание запроса сопряжено с некоторыми накладными расходами.
Поиск выполняется в одном потоке на шарду. Большинство поисковых запросов попадают в несколько шардов. Каждый шард выполняет поиск в одном потоке CPU. Хотя шард может выполнять несколько одновременных поисков, поиск по большому количеству шардов приведет к истощению пула поисковых потоков узла. А это будет причиной для низкой пропускной способности и, соответственно, низкой скорости поиска.
В этот момент пора бы задаться вопросом: каково правильное количество шардов для самой быстрой операции поиска? Если данных мало, то шардов должно быть как можно меньше.

Но сколько данных слишком много? Нелегко определить золотую середину. В общем случае хватит 1 шарда для быстрого поиска, до тех пор, пока большее их количество не начнет более эффективно распараллеливать работу между несколькими потоками. В общем, ответ как всегда «it depends»...
Запись vs. чтение
Как видите, уже есть конфликт. Для быстрой индексации нужно как можно больше шардов, а для быстрого поиска лучше иметь как можно меньше шардов. Вопрос сводится к приоритетам. Вы должны решить, что важнее: быстрая индексация или быстрый поиск. Elasticsearch поддерживает и то, и другое, но когда дело доходит до оптимизации, нужно понять что наиболее важно.
Как определить идеальный размер шарда
1. Сравнительный анализ

Лучший способ определить идеальный размер сегмента — провести эксперименты. Выберите сервер, который максимально идентичен будущему боевому серверу, а затем:

  • Настройте кластер Elasticsearch с 1 узлом.
  • Индексируйте свои производственные данные.
  • Измерьте скорость индексации с помощью инструмента сравнительного анализа (например, Rally).
Если время приема соответствует SLA или сферическим ожиданиям в вакууме, значит, все в порядке, и вы уже знаете будущие настройки. После этого сравните производительность запросов. Если они удовлетворяют требованиям к быстродействию, можно идти в бой. В противном случае нужно повторить бенчмаркинг с большим количеством сегментов или узлов, пока не достигнете своих SLA. Это наиболее точный способ определить размер сегмента конкретно для вашего случая. Если это слишком сложно, можно начать с большего количества шардов и/или большего количества нод и уменьшать масштаб.

2. Общие рекомендации

Эксперименты, конечно, хорошо, но есть также и общие рекомендации:

  • Удалять индексы вместо отдельных документов;
  • Использовать политику управления индексами (ILM);
  • Для логирования обычно хорошо подходят шарды размером от 10 до 50 ГБ, а для операций поиска достаточно 20-25 ГБ;
  • Учитывать общий размер JVM Heap. Нужно стремиться иметь 20 шардов на каждый ГБ JVM Heap;
  • Избегать овершардинга на ноде;
  • Следить за корректностью типов полей.
Удаляйте индексы, а не документы. Удаленные документы не удаляются сразу из файловой системы Elasticsearch. Вместо этого Elasticsearch помечает документ как удаленный на каждом связанном шарде. Отмеченный документ будет продолжать использовать ресурсы до тех пор, пока не будет удален во время периодического слияния сегментов. По возможности, вместо документов удаляйте целые индексы. Elasticsearch сразу же удаляет такие индексы прямо из файловой системы и высвобождает ресурсы.

Используйте механизм Index Lifecycle Management (ILM). Преимущество этого механизма — автоматический перенос индексов, который создает новые индексы, когда текущий соответствует определенному порогу: max_primary_shard_size, max_age, max_docs или max_size. Когда индекс больше не нужен, можно использовать ILM для его автоматического удаления и освобождения ресурсов.

ILM также позволяет легко менять стратегию шардирования с течением времени:

  • Хотите уменьшить количество шардов для новых индексов? Измените параметр index.number_of_shards в соответствующем шаблоне индекса потока данных.
  • Хотите больше шардов или меньше реплицированных индексов? Увеличьте порог переноса в ILM.
  • Нужны индексы, охватывающие более короткие интервалы? Компенсируйте увеличение количества шардов, удаляя старые индексы раньше. Это можно сделать, снизив пороговое значение min_age на фазе удаления удаления.

Управляйте размером шардов. Ключевая причина: большие шарды дольше восстанавливаются после сбоя. Когда нода выходит из строя, Elasticsearch перераспределяет шарды узла по оставшимся нодам с ролью data. Процесс восстановления обычно включает копирование содержимого шарда по сети, поэтому для восстановления шарда размером 100 ГБ потребуется в два раза больше времени, чем для шарда размером 50 ГБ. Напротив, маленькие шарды несут пропорционально больше накладных расходов и менее эффективны для поиска. Поиск по пятидесяти шардам размером 1 ГБ потребует значительно больше ресурсов, чем поиск по одному шарду размером 50 ГБ, содержащего те же данные. Золотую середину никто не отменял.

Жестких ограничений на размер шарда нет, но опыт показывает, что шарды размером от 10 до 50 ГБ обычно хорошо подходят для логов и данных временных рядов. Никто не запрещает использовать более крупные шарды в зависимости от возможностей сети и подходам к использованию Elasticsearch. Шарды меньшего размера могут подойти, например, для корпоративного поиска. Если вы используете ILM, установите пороговое значение max_primary_shard_size для действия переноса равным 50 ГБ, чтобы избежать сегментов размером более 50 ГБ.

Текущий размер шардов можно узнать через API:

GET _cat/shards?v=true&h=index,prirep,shard,store&s=prirep,store&bytes=gb
Значение pri.store.size показывает общий размер всех основных шардов индекса.

index                                 prirep shard store
.ds-my-data-stream-2022.05.06-000001  p      0      50gb
...
Учитывайте размер JVM Heap. Используйте правило 20 шардов на 1 Гб JVM Heap. Количество шардов, которые может хранить нода, пропорционально памяти JVM Heap. Например, нода с 30 ГБ оперативной памяти должна иметь не более 600 шардов. Чем ниже этого порога вы будете держать ноды, тем лучше. Если выяснилось, что на ноде превышаются 20 шардов на ГБ JVM Heap, рассмотрите возможность добавления еще одной ноды.

Некоторые индексы почти пусты и редко используются. Можно, конечно, не учитывать шарды для этих индексов в расчетах, но лучше их удалить. Чтобы проверить настроенный размер JVM Heap на ноде, используйте API:

GET _cat/nodes?v=true&h=heap.max
Либо можно посмотреть количество шардов на ноде:

GET _cat/shards?v=true
Избегайте овершардинга на ноде. Если на определенной ноде скучковалось слишком много шардов, может произойти овершардинг. Например, если одна нода содержит слишком много шардов индекса с большим объемом, на ноде могут возникнуть проблемы. Чтобы предотвратить овершардинг, используйте параметр индекса index.routing.allocation.total_shards_per_node для явного ограничения количества шардов на одном узле. Можно настроить index.routing.allocation.total_shards_per_node, используя API:

PUT my-index-000001/_settings
{
  "index" : {
    "routing.allocation.total_shards_per_node" : 5
  }
}
Избегайте некорректного сопоставления полей (mapping). По умолчанию, Elasticsearch автоматически создает сопоставление для каждого поля в каждом документе, который он индексирует. Каждое отображаемое поле соответствует некоторым структурам данных на диске, которые необходимы для эффективного поиска, извлечения и агрегирования по этому полю. Подробная информация о каждом отображаемом поле также хранится в памяти. Часто эти накладные расходы не нужны, поскольку поле не используется ни при поиске, ни при агрегировании. Используйте статическое сопоставление вместо динамического, чтобы избежать создания полей, которые никогда не используются. Если набор полей обычно используется совместно, посмотрите в сторону использования copy_to для их объединения во время индексирования. Если поле редко используется, может лучше сделать его runtime-полем.

Можно получить информацию о том, какие поля используются с помощью API статистики использования полей. После этого можно проанализировать использование диска сопоставленными полями с помощью API анализа использования диска.


Спасибо за внимание! Надеюсь, статья была для вас полезна. Если хотите больше узнать об Elastic, приходите к нам на семинар-инструктаж. Ниже ссылка на ближайший, а также ссылки на другие наши статьи.
Что дальше

Приглашаем наши тренинги по Zabbix, OpenSearch, ElasticSearch

Максимум знаний за короткое время
Другие наши статьи об Elastic
Статья на Хабре
Статья на Хабре
Статья на Хабре
Статья на Хабре
Есть вопросы или предложения?
Вы можете написать здесь и при необходимости приложить файлы.
Нажимая на кнопку, вы даете согласие на обработку персональных данных и соглашаетесь c политикой конфиденциальности.