MAATRIX / Блог / Сколько RAM нужно для OpenSearch

Сколько RAM нужно для OpenSearch

MAATRIX

OpenSearch — форк Elasticsearch, который AWS выпустила после того, как Elastic сменила лицензию на более закрытую. Движок тот же по сути: Lucene под капотом, JVM, шарды, инвертированные индексы. И память он ест точно так же жадно — новички регулярно ставят его на VPS с 2 ГБ RAM «для теста» и получают либо мгновенный OOM, либо кластер, который зависает на первом же агрегирующем запросе. Разберёмся, куда уходит память в OpenSearch и сколько закладывать под конкретную задачу — полнотекстовый поиск или аналитику логов.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Куда уходит память: heap и файловый кеш

OpenSearch — это Java-процесс (JVM) поверх Lucene, и память у него делится на две принципиально разные части.

JVM heap — то, что вы явно выделяете флагами -Xms/-Xmx. Сюда попадают структуры данных запросов, буферы индексации, часть кеша агрегаций (field data / doc values в старых версиях), состояние кластера, метаданные шардов. Если heap переполняется — начинается частая сборка мусора (GC), запросы подвисают, а при совсем плохом раскладе процесс падает по OutOfMemoryError.

Off-heap память (page cache ОС) — сюда Lucene мапит файлы сегментов индекса через mmap. Это не JVM-память, а обычный дисковый кеш операционной системы. Именно он даёт основной прирост скорости поиска: чем больше «горячих» сегментов помещается в RAM мимо диска, тем быстрее ответ. OpenSearch эту память явно не резервирует — она достаётся ему по остаточному принципу, тем, что не занято heap.

Отсюда ключевое правило сайзинга: heap ≠ вся память сервера. Отдадите под -Xmx 90% RAM — Lucene останется без файлового кеша, и даже простые запросы начнут читать сегменты с диска, производительность просядет в разы несмотря на «щедрый» heap.

Как считать heap: правило 50% и потолок в 32 ГБ

Официальная рекомендация OpenSearch (унаследованная от Elasticsearch) — отдавать под heap не больше 50% физической RAM сервера, а остальное оставлять операционной системе под page cache.

Второе ограничение — технический потолок JVM около 32 ГБ. До этой границы работает механизм compressed oops (сжатые указатели на объекты, 32 бита вместо 64), который экономит память на служебных структурах. Стоит превысить порог — JVM переключается на полные 64-битные указатели, и эффективный объём полезного heap на 30-31 ГБ может оказаться даже ниже, чем на честных 32 ГБ. Поэтому типовые точки для -Xmx: 8, 16, 26, 30, максимум 32 ГБ — не выше.

Практический вывод из обоих правил:

RAM сервераHeap (Xms=Xmx)Что остаётся под page cache
4 ГБ2 ГБ~2 ГБ
8 ГБ4 ГБ~4 ГБ
16 ГБ8 ГБ~8 ГБ
32 ГБ16 ГБ~16 ГБ
64 ГБ30-31 ГБ (не выше!)~33 ГБ
128 ГБ30-31 ГБостальное — page cache, либо разносите на несколько нод

На 128 ГБ RAM поднимать heap до 60 ГБ бессмысленно и вредно — упрётесь в потолок compressed oops и потеряете эффективность. Правильный путь — держать heap на уровне 30-31 ГБ, а остальное отдать под page cache, либо запустить на этом железе две-три ноды OpenSearch с heap по 30 ГБ каждая.

Настраивается heap в файле /etc/opensearch/jvm.options (или через OPENSEARCH_JAVA_OPTS в docker-compose):

-Xms8g
-Xmx8g

Xms и Xmx всегда выставляются равными — это исключает динамическое изменение размера heap «на лету», которое само по себе создаёт паузы GC.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Минимальные конфигурации по задачам

Ниже — ориентировочные конфигурации. Это не измеренные бенчмарки, а рабочие точки отсчёта, от которых стоит отталкиваться и корректировать под свою нагрузку.

Разработка и тесты (single-node, без прод-трафика). От 2 ГБ RAM OpenSearch формально стартует, но с heap в 1 ГБ уже агрегирующий запрос или bulk-индексация на несколько тысяч документов упрётся в circuit breaker («слишком мало памяти для операции»). Комфортный минимум для разработки и CI — 4 ГБ RAM, heap 2 ГБ.

Небольшой прод: сайт с полнотекстовым поиском, десятки тысяч документов. Каталог интернет-магазина, поиск по базе статей блога, одна нода без реплик. Реалистичный минимум — 8 ГБ RAM, heap 4 ГБ. На 4 ГБ RAM теоретически работает, но без запаса на GC-паузы под нагрузкой.

Аналитика логов (ELK-подобная нагрузка), 1 нода. Объём данных обычно больше, чем в поисковом каталоге, индексация идёт непрерывным потоком (OpenSearch как приёмник из Fluent Bit / Logstash / Data Prepper). Стартовая точка — 16 ГБ RAM, heap 8 ГБ, с ротацией индексов по дням/неделям через ISM (Index State Management).

Продакшен-кластер с несколькими нодами. От 32 ГБ RAM на data-ноду, heap 16 ГБ. Здесь уже есть смысл разделять роли — отдельные master-ноды (маленькие, 4-8 ГБ, данные не хранят) и data-ноды с основным объёмом памяти.

Если пока не уверены, какой профиль ваш — почитайте про расчёт ресурсов под базу данных: логика похожая, поисковый движок тоже по сути СУБД с своей спецификой.

Шарды и агрегации: почему память тает с ростом данных

Число шардов — второй по значимости фактор после объёма данных. Каждый шард — отдельный экземпляр Lucene-индекса со своими сегментами и служебными структурами в heap (метаданные сегментов, кеш терминов). Слишком мелкая нарезка индексов — частая ошибка новичков.

Правило, которое стоит держать в голове: избыточные мелкие шарды тратят память впустую. Индекс в день с 5 шардами при 500 МБ данных — это шарды по 100 МБ, каждый из которых всё равно тянет накладные расходы как полноценный Lucene-индекс. Ориентир (не жёсткое правило, а разумный диапазон) — держать размер шарда в пределах 10-50 ГБ и не превышать примерно 20 шардов на 1 ГБ выделенного heap на ноде.

Агрегации (terms, date_histogram, cardinality) на высококардинальных полях — второй источник давления на heap: память под группировку тысяч уникальных значений растёт пропорционально кардинальности, а не размеру индекса. Если поле вроде user_id или request_id с миллионами значений участвует в terms-агрегациях — закладывайте heap с запасом либо ограничивайте size.

Проверить текущее потребление heap по нодам:

curl -s -u admin:admin -k \
  "https://localhost:9200/_nodes/stats/jvm?pretty" \
  | grep -A5 heap_used_percent

Если heap_used_percent регулярно держится выше 75-85% в состоянии покоя (не на пике запроса) — это сигнал добавлять RAM или пересматривать нарезку шардов, а не ждать OOM.

Логи против поиска: разная плотность данных на ГБ RAM

Стоит отдельно проговорить разницу между двумя типичными сценариями использования OpenSearch, потому что требования к памяти у них разные при одинаковом объёме диска.

Полнотекстовый поиск (каталог, документы, база знаний). Данные относительно статичны, индексация происходит редко, зато читающие запросы — постоянно, и им критичен page cache: чем больше индекса помещается в RAM, тем быстрее ответ. Ориентируйтесь на размер индекса на диске: если активно опрашиваемый индекс весит 20 ГБ, комфортно иметь сервер, где под page cache (после вычета heap) остаётся хотя бы 10-15 ГБ, чтобы горячие сегменты не вымывались.

Аналитика логов. Данные пишутся непрерывным потоком (bulk-индексация каждую секунду), читаются в основном последние N дней, старые индексы архивируются или удаляются через ISM-политику. Нагрузка на heap здесь выше на этапе индексации (буферы, merge сегментов), а для чтения важнее не столько абсолютный объём page cache, сколько скорость диска (SSD/NVMe обязателен) и правильная ротация индексов.

Грубый практический ориентир для логовой нагрузки (это именно ориентир, а не измеренный бенчмарк — у вас будет иначе в зависимости от структуры документов и retention): на каждые 20-30 ГБ входящих логов в сутки при retention 14-30 дней комфортно закладывать от 16 ГБ RAM на ноду с ростом по мере накопления истории. При росте потока логов логичнее не раздувать heap одной ноды сверх 30-31 ГБ, а горизонтально добавлять data-ноды.

Если строите связку логов на OpenSearch Dashboards или сравниваете с альтернативами — посмотрите статью Grafana или Kibana: что выбрать для сервера, там разбор смежных инструментов визуализации логов и метрик.

Настройка сервера: swap, memory_lock, лимиты ОС

Память для OpenSearch нужно не только выделить, но и защитить от подкачки — своп для JVM с GC крайне вреден: страница heap, вытесненная в swap, при следующем обращении читается с диска, и это может добавить секунды к паузе GC вместо микросекунд.

1. Запретить своп для процесса OpenSearch. В opensearch.yml:

bootstrap.memory_lock: true

И убедиться, что лимит memlock снят в systemd-юните (/usr/lib/systemd/system/opensearch.service):

[Service]
LimitMEMLOCK=infinity

После правки — systemctl daemon-reload && systemctl restart opensearch. Проверить, что блокировка сработала:

curl -s -u admin:admin -k "https://localhost:9200/_nodes?filter_path=**.mlockall&pretty"

Если вместо bootstrap.memory_lock вы предпочитаете вообще отключить своп на сервере — это тоже рабочий вариант для выделенной под OpenSearch машины, подробности в статье про настройку swap на VPS.

2. Поднять vm.max_map_count. Lucene активно использует memory-mapped файлы, и дефолтного лимита ОС (обычно 65530) не хватает уже на среднем объёме данных — при старте увидите ошибку max virtual memory areas vm.max_map_count too low:

sudo sysctl -w vm.max_map_count=262144
echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.d/99-opensearch.conf

3. Docker/docker-compose. Если OpenSearch в контейнере, bootstrap.memory_lock дополнительно требует ulimits в compose-файле:

services:
  opensearch:
    image: opensearchproject/opensearch:2
    environment:
      - "OPENSEARCH_JAVA_OPTS=-Xms4g -Xmx4g"
      - bootstrap.memory_lock=true
    ulimits:
      memlock:
        soft: -1
        hard: -1
    mem_limit: 8g

mem_limit контейнера должен быть заметно больше -Xmx (в примере выше 8 ГБ лимита против 4 ГБ heap) — контейнеру тоже нужна память под page cache и служебные процессы JVM вне heap.

Признаки нехватки памяти и что делать

Три типовых симптома, что памяти под OpenSearch мало, и что с каждым делать.

Частые долгие GC-паузы. В логах (/var/log/opensearch/<cluster-name>.log) видны записи вида [gc][young] с растущей длительностью, запросы периодически «замирают». Смотреть через _nodes/stats:

curl -s -u admin:admin -k "https://localhost:9200/_nodes/stats/jvm?filter_path=**.gc.collectors.*.collection_time_in_millis&pretty"

Решение — либо добавить RAM и увеличить heap (в рамках правила 50%/32 ГБ), либо уменьшить число/размер шардов, либо ограничить размер результатов агрегаций.

Circuit breaker exceptions. Ошибки вида [parent] Data too large, data for [<agg_name>] would be [X/Y] — OpenSearch сам защищается от OOM, обрывая операцию, которая грозит переполнить heap. Это не баг, а рабочий предохранитель: сигнал, что запрос (обычно тяжёлая агрегация или большой bulk) не помещается в текущий heap. Разбить запрос на части, ограничить size, либо увеличить heap.

OOM killer от ядра Linux. Если суммарное потребление процесса вышло за пределы физической RAM (или mem_limit контейнера) — ядро убивает процесс, в dmesg будет запись Out of memory: Killed process ... (java). Решение одно: RAM физически не хватает под текущую heap + off-heap конфигурацию — уменьшать -Xmx либо переезжать на сервер большего объёма. Общие принципы разбора — в статье что делать при нехватке RAM.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Хватит ли 2 ГБ RAM для OpenSearch?

Формально процесс стартует, но это конфигурация для «посмотреть, что это такое», не для реальной работы — даже небольшая индексация или агрегация упрётся в circuit breaker. Рабочий минимум — 4 ГБ.

Можно ли отдать под heap больше 50% RAM, если данных мало?

Технически можно через -Xmx, но тогда Lucene останется почти без page cache, и поиск начнёт читать сегменты с диска на каждый запрос — на SSD это терпимо, но ощутимо медленнее, чем при холодном железе с горячим кешем в RAM.

Почему heap ограничен 32 ГБ, ведь на сервере 128 ГБ RAM?

Ограничение не про доступную RAM, а про механизм compressed oops в JVM — экономию памяти на указателях, который работает только до этого порога. Выше — либо несколько нод по 30 ГБ heap каждая, либо смириться с менее эффективным использованием памяти на одной большой JVM.

OpenSearch и Elasticsearch одинаково требовательны к памяти?

Да, у обоих в основе Lucene и JVM, правило 50%/32 ГБ и логика heap/off-heap идентичны — можно ориентироваться на одни и те же практики сайзинга независимо от того, какой из двух форков используете.

Своп точно нужно выключать полностью?

Не обязательно выключать на всём сервере, если на нём кроме OpenSearch крутится что-то ещё, — достаточно bootstrap.memory_lock: true, чтобы страницы конкретно JVM-процесса не уходили в swap, при этом остальная система своп использовать может.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →