Настройка для скорости поиска
Предоставьте память кэшу файловой системы
Elasticsearch сильно полагается на кэш файловой системы, чтобы сделать поиск быстрым. В общем случае, вы должны убедиться, что по крайней мере половина доступной памяти выделяется кэшу файловой системы, чтобы Elasticsearch мог хранить горячие области индекса в физической памяти.
Избегайте переполнения кэша страниц, используя умеренные значения readahead в Linux
Поиск может вызывать множество случайных операций ввода-вывода. Когда у базового блочного устройства высокое значение readahead, может быть выполнено много ненужных операций ввода-вывода, особенно когда к файлам осуществляется доступ с помощью отображения памяти (см. типы хранилищ).
Большинство дистрибутивов Linux используют разумное значение readahead в 128KiB для одного обычного устройства, однако, при использовании программного RAID, LVM или dm-crypt результирующее блочное устройство (поддерживающее Elasticsearch path.data) может иметь очень большое значение readahead (в диапазоне нескольких МБ). Это обычно приводит к сильному переполнению кэша страниц (файловой системы), негативно влияющему на производительность поиска (или обновления).
Вы можете проверить текущее значение в KiB, используя lsblk -o NAME,RA,MOUNTPOINT,TYPE,SIZE. Обратитесь к документации вашего дистрибутива, чтобы узнать, как изменить это значение (например, с помощью правила udev для сохранения после перезагрузки или через blockdev --setra как временное значение). Мы рекомендуем значение 128KiB для readahead.
blockdev ожидает значения в секторах по 512 байт, в то время как lsblk сообщает значения в KiB. В качестве примера, чтобы временно установить readahead на 128KiB для /dev/nvme0n1, укажите blockdev --setra 256 /dev/nvme0n1.
Используйте более быстрое оборудование
Если ваш поиск ограничен операциями ввода-вывода, вы должны рассмотреть возможность выделения больше памяти кэшу файловой системы (см. выше) или покупки более быстрых дисков. В частности, SSD-диски известны своей большей производительностью по сравнению с дисками с вращающимися пластинами. Всегда используйте локальное хранилище, удаленные файловые системы, такие как NFS или SMB, следует избегать. Также будьте осторожны с виртуализованным хранилищем, таким как Amazon’s Elastic Block Storage. Виртуализованное хранилище работает очень хорошо с Elasticsearch, и оно привлекательно из-за высокой скорости и простоты настройки, но, к сожалению, оно неизбежно медленнее в долгосрочной перспективе по сравнению с выделенным локальным хранилищем. Если вы размещаете индекс на EBS, обязательно используйте выделенные IOPS, иначе операции могут быстро быть ограничены.
Если ваш поиск ограничен процессором, вы должны рассмотреть возможность покупки более быстрых процессоров.
Моделирование документов
Документы должны быть смоделированы таким образом, чтобы операции во время поиска были максимально дешевыми.
В частности, следует избегать объединений. nested может сделать запросы на несколько порядков медленнее, а отношения «родитель-потомок» могут сделать запросы в сотни раз медленнее. Поэтому, если те же вопросы можно ответить без объединений, денормализовав документы, можно ожидать существенного повышения скорости.
Использовать как можно меньше полей для поиска
Чем больше полей захватывает запрос query_string или multi_match, тем медленнее он работает. Общей техникой для повышения скорости поиска по нескольким полям является копирование их значений в одно поле во время индексирования, а затем использование этого поля во время поиска. Это можно автоматизировать с помощью директивы copy-to отображений без необходимости изменения исходных документов. Вот пример индекса, содержащего фильмы, который оптимизирует запросы, которые ищут по имени и сюжету фильма, индексируя оба значения в поле name_and_plot.
PUT movies
{
"mappings": {
"properties": {
"name_and_plot": {
"type": "text"
},
"name": {
"type": "text",
"copy_to": "name_and_plot"
},
"plot": {
"type": "text",
"copy_to": "name_and_plot"
}
}
}
} Предварительная индексация данных
Вы должны использовать закономерности в своих запросах для оптимизации способа индексирования данных. Например, если у всех ваших документов есть поле price и большинство запросов выполняют агрегации range по фиксированному списку диапазонов, вы можете ускорить эту агрегацию, предварительно индексируя диапазоны в индекс и используя агрегации terms.
Например, если документы выглядят так:
PUT index/_doc/1
{
"designation": "spoon",
"price": 13
} и запросы на поиск выглядят так:
GET index/_search
{
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 10 },
{ "from": 10, "to": 100 },
{ "from": 100 }
]
}
}
}
} Тогда документы можно дополнить полем price_range во время индексирования, которое должно быть отображено как keyword:
PUT index
{
"mappings": {
"properties": {
"price_range": {
"type": "keyword"
}
}
}
}
PUT index/_doc/1
{
"designation": "spoon",
"price": 13,
"price_range": "10-100"
} И затем запросы на поиск могут агрегировать это новое поле, а не выполнять агрегацию range по полю price.
GET index/_search
{
"aggs": {
"price_ranges": {
"terms": {
"field": "price_range"
}
}
}
} Рассмотрите отображение идентификаторов как keyword
Не все числовые данные следует отображать как тип данных числовой. Elasticsearch оптимизирует числовые поля, такие как integer или long, для запросов range. Однако, поля типа keyword лучше подходят для запросов term и других запросов уровня терминов.
Идентификаторы, такие как ISBN или идентификатор продукта, редко используются в запросах range. Однако они часто извлекаются с помощью запросов уровня терминов.
Рассмотрите отображение числового идентификатора как keyword, если:
Если вы не уверены, какой использовать, вы можете использовать многопольное отображение, чтобы отобразить данные как keyword и как числовой тип данных.
Избегайте скриптов
По возможности избегайте использования сортировки на основе скриптов, скриптов в агрегациях и запроса script_score. См. Скрипты, кэширование и скорость поиска.
Поиск округленных дат
Запросы к полям даты, использующие now, обычно не кэшируются, так как диапазон, который сопоставляется, постоянно изменяется. Однако переход к округленной дате часто приемлем с точки зрения пользовательского опыта и имеет преимущество в лучшей работе с кэшем запросов.
Например, запрос ниже:
PUT index/_doc/1
{
"my_date": "2016-05-11T16:30:55.328Z"
}
GET index/_search
{
"query": {
"constant_score": {
"filter": {
"range": {
"my_date": {
"gte": "now-1h",
"lte": "now"
}
}
}
}
}
} можно заменить следующим запросом:
GET index/_search
{
"query": {
"constant_score": {
"filter": {
"range": {
"my_date": {
"gte": "now-1h/m",
"lte": "now/m"
}
}
}
}
}
} В этом случае мы округлили до минуты, поэтому, если текущее время 16:31:29, запрос диапазона будет соответствовать всему, значение поля my_date которого находится между 15:31:00 и 16:31:59. И если несколько пользователей выполняют запрос, содержащий этот диапазон в течение одной минуты, кэш запросов может немного ускорить процесс. Чем длиннее интервал, используемый для округления, тем больше кэш запросов может помочь, но следует помнить, что слишком агрессивное округление также может навредить пользовательскому опыту.
Может возникнуть искушение разделить диапазоны на большую кэшируемую часть и меньшие некэшируемые части, чтобы использовать кэш запросов, как показано ниже:
GET index/_search
{
"query": {
"constant_score": {
"filter": {
"bool": {
"should": [
{
"range": {
"my_date": {
"gte": "now-1h",
"lte": "now-1h/m"
}
}
},
{
"range": {
"my_date": {
"gt": "now-1h/m",
"lt": "now/m"
}
}
},
{
"range": {
"my_date": {
"gte": "now/m",
"lte": "now"
}
}
}
]
}
}
}
}
} Однако, такая практика может сделать запрос медленнее в некоторых случаях, поскольку накладные расходы, вводимые запросом bool, могут свести на нет выгоду от лучшего использования кэша запросов.
Принудительное слияние только для чтения индексов
Индексы, которые только для чтения, могут извлечь выгоду из слияния до одного сегмента. Это обычно происходит с индексами, основанными на времени: только индекс для текущего временного интервала получает новые документы, а более старые индексы только для чтения. Фрагменты, которые были принудительно слиты в один сегмент, могут использовать более простые и эффективные структуры данных для выполнения поиска.
Не принудительно сливайте индексы, в которые вы по-прежнему записываете или в которые вы будете писать в будущем. Вместо этого, полагайтесь на автоматический фоновый процесс слияния, чтобы выполнять слияния по мере необходимости для бесперебойной работы индекса. Если вы продолжаете запись в принудительно объединенный индекс, его производительность может значительно ухудшиться.
Подготовить глобальные ординалы
Глобальные ординалы — это структура данных, используемая для оптимизации производительности агрегаций. Они вычисляются лениво и хранятся в куче JVM как часть кэша данных полей. Для полей, которые активно используются в агрегациях по категориям, вы можете указать Elasticsearch на создание и кэширование глобальных ординалов до получения запросов. Это следует делать осторожно, так как это увеличит использование кучи и может замедлить операции обновления. Параметр можно обновить динамически для существующего отображения, установив параметр eager global ordinals:
PUT index
{
"mappings": {
"properties": {
"foo": {
"type": "keyword",
"eager_global_ordinals": true
}
}
}
} Подготовить кэш файловой системы
Если машина, на которой работает Elasticsearch, перезагружается, кеш файловой системы будет пустым, поэтому потребуется некоторое время, прежде чем операционная система загрузит горячие области индекса в память, чтобы операции поиска были быстрыми. Вы можете явно указать операционной системе, какие файлы следует загружать в память с готовностью, в зависимости от расширения файла, используя настройку index.store.preload.
Загрузка данных в кеш файловой системы с готовностью для слишком многих индексов или слишком многих файлов сделает поиск медленнее, если кеш файловой системы недостаточно велик для хранения всех данных. Используйте с осторожностью.
Использование сортировки индекса для ускорения конъюнкций
Сортировка индекса может быть полезна для ускорения конъюнкций за счет немного более медленной индексации. Подробнее об этом можно прочитать в документации по сортировке индекса.
Использование preference для оптимизации использования кеша
Существует несколько кешей, которые могут помочь в производительности поиска, таких как кеш файловой системы, кеш запросов или кеш запросов. Однако все эти кеши поддерживаются на уровне узла, что означает, что если вы выполните один и тот же запрос дважды подряд, имеете 1 или более реплики и используете round-robin, алгоритм маршрутизации по умолчанию, то эти два запроса попадут в разные копии фрагментов, предотвращая использование кешей на уровне узла.
Поскольку для пользователей приложения поиска часто выполняются похожие запросы один за другим, например, для анализа более узкого подмножества индекса, использование значения предпочтения, идентифицирующего текущего пользователя или сеанс, может помочь оптимизировать использование кешей.
Реплики могут помочь с пропускной способностью, но не всегда
В дополнение к повышению устойчивости реплики могут помочь улучшить пропускную способность. Например, если у вас есть индекс с одним фрагментом и тремя узлами, вам нужно установить количество реплик в 2, чтобы в итоге иметь 3 копии вашего фрагмента, чтобы все узлы использовались.
Теперь представьте, что у вас есть индекс из 2 фрагментов и двух узлов. В одном случае количество реплик равно 0, что означает, что каждый узел содержит один фрагмент. Во втором случае количество реплик равно 1, что означает, что каждый узел имеет два фрагмента. Какая настройка будет работать лучше с точки зрения производительности поиска? Обычно та настройка, в которой меньше фрагментов на узел в целом, будет работать лучше. Причина в том, что она предоставляет большую долю доступного кеша файловой системы каждому фрагменту, а кеш файловой системы, вероятно, является основным фактором производительности Elasticsearch. В то же время следует учитывать, что настройка без реплик подвержена сбоям в случае сбоя одного узла, поэтому существует компромисс между пропускной способностью и доступностью.
Так какое же нужно количество реплик? Если у вас есть кластер, у которого есть num_nodes узлов, num_primaries первичных фрагментов в целом и если вы хотите иметь возможность справиться с max_failures сбоями узлов одновременно, то правильное количество реплик для вас - max(max_failures, ceil(num_nodes / num_primaries) - 1).
© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/tune-for-search-speed.html