Оптимизация производительности Elasticsearch (шарды, реплики, refresh_interval)

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Оптимизация производительности Elasticsearch (шарды, реплики, refresh_interval)
Сложная
~2-3 рабочих дня
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Оптимизация производительности Elasticsearch (шарды, реплики, refresh_interval)

Медленный Elasticsearch — почти всегда результат неправильных настроек, а не недостатка железа. Лишние шарды убивают производительность надёжнее, чем слабые процессоры. Слишком частый refresh делает индексацию в 3–5 раз медленнее, чем нужно. Оптимизация Elasticsearch — это прежде всего правильное проектирование, а потом уже тюнинг железа.

Шарды: главная точка оптимизации

Каждый шард — это отдельный экземпляр Lucene индекса со своими файловыми дескрипторами, JVM объектами, overhead на heap. На кластере с 5 узлами держать 500 маленьких индексов по 50 шардов каждый = 25 000 шардов = кластер ползает.

Правило: 1 шард = 10–50 GB данных. Меньше — шарды слишком маленькие (overhead доминирует над данными). Больше — шард сложно перебалансировать при добавлении узла.

Максимум шардов на 1 GB heap: ~20 шардов. При heap 16 GB = не более 320 шардов на узел.

Проверить статистику шардов:

# Размер шардов и их распределение
curl -u elastic:pw "http://localhost:9200/_cat/shards?v&h=index,shard,prirep,state,docs,store,node"

# Сколько шардов на узел
curl -u elastic:pw "http://localhost:9200/_cat/nodes?v&h=name,shards,diskUsed,heapPercent"

Уменьшение числа шардов через shrink API:

# Сначала отключить запись и переместить все шарды на один узел
PUT /products/_settings
{
  "settings": {
    "index.routing.allocation.require._name": "es-node-01",
    "index.blocks.write": true
  }
}

# Shrink до 1 шарда
POST /products/_shrink/products_shrunk
{
  "settings": {
    "index.number_of_shards": 1,
    "index.number_of_replicas": 1,
    "index.routing.allocation.require._name": null,
    "index.blocks.write": null
  }
}

refresh_interval

Elasticsearch по умолчанию делает refresh каждую секунду — создаёт новый сегмент Lucene из буфера в памяти и делает документы доступными для поиска. Каждый refresh — файловые операции, создание сегмента, нагрузка на IO.

Для real-time поиска (чат, уведомления) — оставить 1s.

Для аналитики, логов, ETL — увеличить до 30s–300s:

PUT /logs-*/_settings
{
  "index.refresh_interval": "60s"
}

При bulk-загрузке данных — отключить на время:

PUT /products/_settings
{
  "index.refresh_interval": "-1"
}

# Загружаем данные...

PUT /products/_settings
{
  "index.refresh_interval": "1s"
}

POST /products/_refresh

Прирост скорости индексации при refresh_interval: -1 vs 1s — 3–5x.

Merge Policy и forcemerge

Lucene периодически объединяет мелкие сегменты в крупные (merge). Это освобождает место от удалённых документов и ускоряет поиск (меньше сегментов = меньше итераций). По умолчанию происходит в фоне, но создаёт IO нагрузку.

Для read-only индексов (архивные данные, завершённые rolling-индексы) — форсировать merge до 1 сегмента:

POST /logs-2024.01.01/_forcemerge?max_num_segments=1

После forcemerge поиск по индексу значительно быстрее, а размер уменьшается на 20–40% за счёт удаления tombstone-записей для deleted docs.

Не запускать forcemerge на активно индексируемых индексах — создаёт огромную IO нагрузку.

Реплики при индексации

Реплика — синхронная копия шарда на другом узле. Каждый записанный документ индексируется в primary + все replica шарды. При 2 репликах — утроенная нагрузка на диск при индексации.

При bulk-загрузке данных в новый индекс:

// 1. Устанавливаем 0 реплик на время загрузки
PUT /products/_settings
{ "index.number_of_replicas": 0 }

// 2. Загружаем данные
// ...

// 3. Восстанавливаем реплики
PUT /products/_settings
{ "index.number_of_replicas": 1 }

Прирост скорости — 2–3x при 1 реплике, 3–4x при 2 репликах.

Bulk API

Индексировать по одному документу — антипаттерн. Batch-размер зависит от размера документов: цель — пакеты по 5–15 MB.

from elasticsearch import Elasticsearch
from elasticsearch.helpers import bulk, parallel_bulk

es = Elasticsearch([...])

def generate_actions(data):
    for item in data:
        yield {
            "_index": "products",
            "_id": item["id"],
            "_source": item,
        }

# Parallel bulk с несколькими потоками
success, errors = 0, 0
for ok, info in parallel_bulk(
    es,
    generate_actions(data),
    thread_count=4,
    chunk_size=500,
    max_chunk_bytes=10 * 1024 * 1024,  # 10 MB
    raise_on_error=False
):
    if ok:
        success += 1
    else:
        errors += 1
        print(f"Error: {info}")

Оптимизация запросов

Filter vs. Query: использовать filter везде, где не нужен score. Фильтры кэшируются на уровне шарда, не влияют на scoring.

// Медленно (scoring + no cache)
{
  "query": {
    "term": { "is_active": true }
  }
}

// Быстро (no scoring + cached)
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "is_active": true } }
      ]
    }
  }
}

Wildcard и regexp — дорогостоящие операции, особенно с leading wildcard (*term). Избегать или заменять на edge N-gram анализ.

Deep pagination: from: 10000 — дорого. ES должен собрать 10 000 + size документов с каждого шарда. Использовать search_after для пагинации:

{
  "query": { "match_all": {} },
  "sort": [
    { "created_at": "desc" },
    { "_id": "asc" }
  ],
  "search_after": ["2024-01-15T10:00:00", "abc123"],
  "size": 20
}

Мониторинг производительности запросов

Profile API — детальный разбор выполнения запроса:

POST /products/_search
{
  "profile": true,
  "query": {
    "match": { "title": "ноутбук" }
  }
}

В ответе — breakdown по каждому шарду: время создания запроса, scoring, fetch. Позволяет найти узкое место.

Hot Threads API — что делает JVM:

curl -u elastic:pw "http://localhost:9200/_nodes/hot_threads"

Heap и GC

При heap > 85% включается агрессивный GC, запросы начинают тормозить. Признаки: GCOverheadLimit исключения в логах, резкое снижение throughput.

Смотреть GC статистику:

curl -u elastic:pw "http://localhost:9200/_nodes/stats/jvm?pretty" | \
  jq '.nodes[] | {name: .name, heap_used_percent: .jvm.mem.heap_used_percent, gc_young: .jvm.gc.collectors.young.collection_time_in_millis}'

G1GC (по умолчанию в JDK 14+) — лучший выбор для ES. В jvm.options:

-XX:+UseG1GC
-XX:G1ReservePercent=25
-XX:InitiatingHeapOccupancyPercent=30

Сроки

Аудит конфигурации существующего кластера с рекомендациями — 1 рабочий день. Оптимизация шардинга, refresh_interval, bulk-индексации — 2–3 дня. Глубокая оптимизация запросов с profiling на реальной нагрузке — дополнительно 1–2 дня.