Подборка советов по ускорению процесса индексирования с Elasticsearch.
Производительность
- Установите максимальный размер открытых дескрипторов файла для пользователя от 32k до 64k
- Если возможно, отключите свопинг памяти для процессов Elasticsearch.
- Установите значение -Xms равным -Xmx (то же самое что установить значение переменной среды ES_HEAP_SIZE).
- Оставьте некоторое количество памяти, чтобы кеш операционной системы мог использовать для Lucene.
- Elasticsearch JVM не должен занимать больше половины всего объема памяти.
- Оптимизируйте настройки сервера Elasticsearch
Маппинг
- По умолчанию Elasticsearch хранит оригинальные данные в поле _source_field. (при необходимости это можно отключить)
- По умолчанию Elasticsearch анализирует входные данные всех полей и хранит их в поле _all_field. (при необходимости это можно отключить)
- Если вы используете _source_field, то нет смысла устанавливать поля _stored
- Если вы не используете _source_field, то устанавливайте в _stored только необходимые вам поля.
Использование _source дает некоторые преимущества, например, доступность использования API для обновления
- Задумайтесь, насколько вам необходимы нормы (norms) для анализируемых полей. Если в них нет необходимости, то отключите их, установив norms.enabled в false
- Используйте самый простой анализатор, который соответствует вашим требованиям.
- Не анализируйте, не храните и вообще даже не посылайте данные в Elasticsearch, если они вам не нужны в поиске.
Запросы и клиенты
Можно получить значительное увеличение производительности путем оптимизации способа передачи запросов к Elasticsearch:
- Отправка отдельных запросов на каждый документ заничтельно снижает производительность (_doc), документы необходимо собирает в один пакет (буфер) и отправлять на индексировать одним запросом (_bulk)
- Оптимизируйте размер bulk запроса (пакетный запрос), то есть определите количество документов для одного запроса. (опытным путем)
- Рассмотрите существующие клиенты (фреймворки), они могут работать гораздо быстрее чем HTTP API
- Если ваш клиент работает на Java, то попробуйте NodeClient или TransportClient. Они подключаются к кластеру и определяет, к каким узлам далее подключаться по определенным запросам, что может сэкономить время при ошибочном выборе узла.
- Можете распараллелить индексацию путем использования нескольких клиентов (подключений). Обычно использование одного подключения является узким местом, так как клиенту требуется время на формирование запроса, его отправку и подтверждение получения данных от Elasticsearch, который в свою очередь, может обрабатывать эти запросы параллельно.
Шардинг и Репликация
Рекомендованный метод для масштабирования Elasticsearch является шардинг (сегментирование) и репликация. Весь необходимый для этого функционал уже есть в Elasticsearch.
- Если вам не хватает одного сервера для обеспечения желаемой производительности, следует рассмотреть вариант масштабирования системы. Несколько узлов в кластере позволяют распараллелить индексирование путем сегментации. Количество сегментов должно быть установлено при создании индекса, в дальнейшем это число неизменно. В случае, когда объем данных заранее неизвестен, можно рассмотреть перенос нескольких сегментов, чтобы иметь несколько в запасе. В качестве альтернативы можно использовать псевдонимы индексов.
- Репликация также является отличным методом борьбы со сбоями, но чем больше реплик вы используете, тем медленнее будет проходить индексация. Хотя для максимального быстродействия стоит полностью отказаться от реплик. В отличие от сегментов, количество реплик можно изменять в любое время, что дает нам ряд вариантов использования. В некоторых случаях, например генерация нового индекса или перенос данных из одного индекса в другой, есть смысл начать вовсе без реплик, добавляя их после окончания критических стадий индексирования.
- Старайтесь отделить узлы данных (которые действительно хранят и индексируют данные) от агрегатных узлов (которые отвечают за выборку данных). Когда агрегатные узлы используются только для обработки поисковых запросов, они лишний раз не обращаются к узлам с данными, таким образом последние могут больше ресурсов уделить индексированию.
- По умолчанию индексирующий запрос считается оконченным только после того, как данные были удачно получены каждой репликой. Если установить параметр query replication в async, то запрос будет считаться законченным, после того как данные будут получены главным сегментом.
Настройки индекса
Существует несколько уровней настроек для индекса, которые позволяют улучшить производительность:
- По умолчанию индексный сегмент обновляет данные каждую секунду, то есть новые документы появляются в индексе с интервалом в одну секунду. Хотя с первого взгляда операция может показаться не очень ресурсоемкой, но она оказывает свое влияние на быстродействие. Таким образом, в зависимости от требований к системе, стоит рассмотреть повышение интервала обновления. Иногда имеет смысл полностью отключить обновление на время индексации, а после её окончания включить его обратно.
- По сравнению с обновлением, сохранение лога транзакций выглядит действительно ресурсоемким процессом. Elasticsearch запускает процесс сохранения основываясь на ряде триггеров, которые можно изменять в процессе работы. Задерживая сохранения или отменяя его полностью, вы можете увеличить скорость индексирования. Помните о том, что в итоге задержанное сохранение займет больше времени, чем плановое.
- Политика объединения по умолчанию поддерживает составной формат данных, то есть данные хранятся в меньшем количестве файлов для сокращения числа открытых файлов. Но при таком подходе мы получим небольшое понижение производительности. Существует два параметра, index.compound_on_flush и index.compount_format, которые указывают должен ли такой подход быть применен к новым сегментам или объединённым соответственно. При установке обоих параметров в false мы получим прирост производительности, но будем иметь большее число открытых файлов
- Объединение сегментов выполняется в фоновом режиме, но требует I\O операций, что влияет на производительность. Допускается увеличение числа максимального количества байт в секунду при объединении на уровне узла или индекса. Обратите внимание, что это уже происходит по умолчанию, но может стоит изменить ограничения в зависимости от ваших потребностей.
- Параметр indices.memory.index_buffer_size определяет процентное соотношение разрешённого к использованию объема динамической памяти для операций индексирования (оставшийся объем будет использован для операций поиска). По умолчанию это значение равно 10%, что может оказаться недостаточным при больших объемах индексации
Разогрев индекса полезен для ускорения поисковых запросов, но при индексации больших объемов данных (особенно при групповой индексации), есть смыл вовсе отключить эту функцию. - Увеличьте размер пула потоков на уровне узла для обычной и групповой индексации. Параметр index.index_concurrency ограничивает количество параллельных индексирующих потоков для одного сегмента. Увеличьте это значение, особенно если в узле нет других сегментов.