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

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

MAATRIX

Typesense — быстрый и простой в установке поисковый движок с типизированными схемами, и в этом его сила: в отличие от классических БД он держит весь рабочий индекс в оперативной памяти. Отсюда и главный вопрос при выборе сервера — не «сколько ядер», а «сколько гигабайт RAM», потому что нехватка памяти здесь не замедляет поиск, а роняет процесс целиком. Разберём, из чего складывается объём памяти и как прикинуть его для вашей коллекции ещё до покупки сервера.

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

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

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

Как Typesense использует память

Typesense написан на C++ и спроектирован как in-memory поисковый движок: все структуры, нужные для быстрого поиска — токенизированные строковые индексы, числовые индексы для сортировки и диапазонных фильтров, счётчики фасетов, инфикс-индексы — живут в оперативной памяти процесса всё время, пока сервер запущен. Это и даёт характерную для Typesense задержку поиска в единицы-десятки миллисекунд даже на многомиллионных коллекциях.

Персистентность при этом обеспечивает RocksDB на диске: туда пишется WAL (write-ahead log) и периодические снапшоты, чтобы при перезапуске восстановить индекс без потери данных. Но диск здесь — про надёжность и восстановление, а не про экономию памяти во время работы: пока Typesense отвечает на запросы, он делает это из RAM. Отсюда следствие — если процесс убьёт OOM-killer, сервер не «подвиснет и отдышится», а перезапустится и будет заново поднимать индекс из данных на диске, и это время пропорционально объёму коллекции. Диск (даже NVMe) важен для скорости восстановления и надёжности записи, а не для снижения требований к RAM — экономить на памяти, рассчитывая на диск как буфер, как в PostgreSQL с его buffer cache, здесь не получится.

Из чего складывается объём RAM

Объём памяти зависит не только от размера исходных JSON-документов, но и от того, как вы описали схему коллекции. На практике на итоговый расход влияют:

  • Количество и тип полей — каждое проиндексированное строковое поле хранит токены и позиции для полнотекстового поиска; числовые и булевы поля — компактнее.
  • facet: true — под фасетное поле Typesense строит отдельную структуру для быстрого подсчёта количества документов по каждому значению. Чем выше кардинальность значений (много уникальных категорий, тегов, брендов), тем больше памяти уходит на фасет.
  • sort: true — сортируемые поля хранятся в отдельных отсортированных массивах, это дополнительный расход поверх обычного индекса.
  • infix: true — инфиксный поиск (поиск по подстроке в середине слова) строит утяжелённую структуру и стоит заметно дороже обычной индексации по префиксу.
  • index: false — поле сохраняется в документе и возвращается в результатах, но не участвует в поиске и не индексируется, это способ сэкономить память на полях, которые нужны только для отображения (например, длинное description).
  • Векторные поля (float[] с num_dim) — под эмбеддинги строится HNSW-индекс, и это отдельная, обычно самая прожорливая статья расхода, если вы делаете семантический поиск или RAG.
  • Количество коллекций и реплик — если вы поднимаете кластер Typesense для отказоустойчивости (Raft, обычно 3 ноды), учтите: это не шардирование, каждая нода хранит полную копию данных в своей RAM. Кластер даёт отказоустойчивость и масштабирование на чтение, но не снижает требование к памяти на нодy — оно остаётся тем же, что и для одной ноды.

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

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

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

Практическая прикидка: сколько RAM закладывать

Точной формулы, которая подходит всем, не существует — слишком много зависит от схемы. Но для старта можно использовать грубый ориентир: реальный расход RAM обычно оказывается в 1.5–3 раза больше «сырого» объёма JSON, который вы отправляете на индексацию, — за счёт токенизации, индексов для фасетов/сортировки и служебных структур. Это именно ориентир для прикидки, а не гарантированное число — на вашей схеме и данных множитель может выйти как меньше, так и заметно больше, особенно если у вас много facet-полей с высокой кардинальностью или инфиксный поиск.

Отталкиваясь от этого, вот грубая таблица для планирования (без учёта векторных полей, о них — в следующем разделе):

Объём исходных JSON-данныхДокументов (ориентир)RAM для стартаКомментарий
до 100 МБдо ~100 тыс.1–2 ГБпростая схема, минимум facet-полей
100 МБ – 1 ГБ100 тыс. – 1 млн2–4 ГБесть facet/sort, инфикса нет
1–5 ГБ1–5 млн4–8 ГБактивные фасеты, часть полей с infix
5–20 ГБ5–20 млн8–16 ГБкрупный каталог/логи, много facet-полей
20+ ГБ20+ млнот 16–32 ГБстоит подумать о разбиении на коллекции или кластер

Простой способ проверить прикидку на своих данных, не покупая сервер заранее: поднять Typesense на тестовой VPS с запасом памяти, залить репрезентативную выборку (10–20% реальных данных) и посмотреть фактический расход через /metrics.json:

curl -H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
  http://localhost:8108/metrics.json | jq .

В ответе будут поля вида typesense_memory_resident_bytes, typesense_memory_active_bytes, typesense_memory_allocated_bytes (Typesense использует jemalloc и отдаёт его статистику) — по резидентной памяти на частичном датасете легко экстраполировать на полный объём, умножив пропорционально количеству документов.

Векторный поиск и embeddings: отдельная статья расходов

Если вы используете Typesense для семантического поиска или RAG-пайплайна с полем эмбеддингов (type: "float[]", num_dim: N), закладывайте память на HNSW-индекс отдельно от «обычного» текстового индекса — это, как правило, самая заметная статья расхода в такой схеме.

Грубая прикидка: сами векторы в float32 занимают num_dim × 4 байта на документ, а HNSW-граф поверх них добавляет накладные расходы — обычно называют диапазон в полтора-два раза больше «сырых» векторов, в зависимости от параметров графа (M, ef_construction) и вашей библиотеки. Возьмём пример:

1 000 000 документов × 384 измерения × 4 байта = 1,536 ГБ — сами векторы
С учётом HNSW-графа (× 1.5–2):              ≈ 2.3–3 ГБ — только под этот индекс

Это уже отдельно от памяти под текстовые поля той же коллекции — цифры складываются, а не берётся максимум. На коллекции из нескольких миллионов документов с эмбеддингами размерности 768–1536 счёт легко уходит за 10–20 ГБ только под векторную часть — проверяйте на своих реальных num_dim и объёме, приведённые множители — ориентир, а не гарантия. Если параллельно считаете память под векторную БД для того же проекта, у нас есть похожий разбор: сколько RAM нужно для Qdrant.

Мониторинг, защита от OOM и оптимизация схемы

Поскольку Typesense — не тот процесс, который «подвинется» при нехватке памяти, а тот, который будет убит планировщиком, здесь особенно важны запас и мониторинг, а не постфактум-диагностика. Что стоит настроить с самого начала:

  • Запас 20–30% сверх расчётного объёма — под пиковую нагрузку при импорте, временный рост коллекции, служебные процессы ОС.
  • Алерт по резидентной памяти процесса, а не только по free -h — смотрите typesense_memory_resident_bytes из /metrics.json или RSS процесса через ps/top, порог тревоги — 75–80% от лимита.
  • Ограничение контейнера через cgroup, если Typesense в Docker — так вы получаете предсказуемый OOM-kill контейнера, а не борьбу за память со всей системой:
docker run -d \
  --name typesense \
  --restart unless-stopped \
  -p 8108:8108 \
  -v /opt/typesense/data:/data \
  --memory=4g \
  typesense/typesense:latest \
  --data-dir /data \
  --api-key=ЗАМЕНИТЕ_НА_СВОЙ_КЛЮЧ \
  --enable-cors

(в проде зафиксируйте конкретный тег образа, а не :latest, чтобы обновление не прилетело неожиданно).

  • Не полагайтесь на swap как на способ «дотянуть» память. Небольшой swap как страховка от жёсткого OOM-килла — нормальная практика, но если Typesense начинает в него реально уходить, задержки поиска взлетают на порядки — вы теряете то, ради чего выбрали in-memory движок. Разбор темы: правильный размер swap для VPS — держите его строго как аварийный буфер, а не как рабочее продолжение RAM.

Дальше — способы реально снизить расход без смены движка:

  • index: false на полях, нужных только для отображения (длинные описания, HTML) — поле хранится и возвращается в ответе, но не индексируется и не съедает память под токены.
  • facet: true только там, где нужна фасетная навигация — фасет по полю с сотнями тысяч уникальных значений стоит непропорционально дорого.
  • infix: true точечно, только там, где реально нужен поиск по подстроке внутри слова (например, по артикулам) — одна из самых тяжёлых опций по памяти.
  • Разумные батчи при импорте через /collections/:collection/documents/import — большой батч даёт временный всплеск памяти, следите за /metrics.json во время первой полной загрузки.

Пример схемы с уже применёнными советами:

{
  "name": "products",
  "fields": [
    { "name": "title", "type": "string" },
    { "name": "description", "type": "string", "index": false },
    { "name": "category", "type": "string", "facet": true },
    { "name": "price", "type": "float", "facet": true, "sort": true },
    { "name": "sku", "type": "string", "infix": true },
    { "name": "in_stock", "type": "bool" }
  ],
  "default_sorting_field": "price"
}

Под полный текстовый индекс и инфикс идёт только title и sku, description хранится, но не индексируется, а фасеты ограничены действительно нужными полями навигации.

Выбор сервера и масштабирование

Поскольку кластер Typesense — это про отказоустойчивость и распределение чтения, а не про снижение требований к памяти на ноду, вертикальный апгрейд (больше RAM на той же ноде) обычно куда практичнее горизонтального для одной растущей коллекции. Три ноды с полной копией данных в HA-кластере имеют смысл, когда вам нужна устойчивость к падению ноды и запас по QPS на чтение — но каждая из них должна вмещать в память весь тот же объём, что и одиночный сервер.

При выборе VPS или выделенного сервера под Typesense практический ориентир такой:

  • Тестовые и небольшие проекты (до сотен тысяч документов, без векторов) — хватает 2–4 ГБ RAM, 2 vCPU.
  • Продакшн-каталог среднего интернет-магазина или база документов на несколько миллионов записей — 8–16 ГБ RAM с запасом под пики импорта.
  • Семантический поиск/RAG с эмбеддингами на миллионы документов — от 16–32 ГБ, здесь часто выгоднее сразу смотреть на выделенный сервер, а не тянуть VPS до предела.

CPU для Typesense обычно вторичен по сравнению с RAM — движок неплохо параллелит запросы по ядрам, но узким местом почти всегда становится именно память, а не процессор. Если вы ещё прикидываете общий запас памяти для сервера с несколькими сервисами, а не только под Typesense, полезно заглянуть в общий разбор: сколько оперативной памяти закладывать с запасом. А если рассматривали Meilisearch как альтернативу с похожей философией «простая установка, типизированные схемы», у нас есть пошаговая установка для сравнения: Meilisearch на Ubuntu 24.04 — архитектурно движки похожи, но конкретные требования к памяти на одинаковых данных могут отличаться.

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

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

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

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

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

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

Хватит ли VPS с 2 ГБ RAM для старта с Typesense?

Да, для небольшого каталога — до сотни тысяч документов с простой схемой без объёмных фасетов и без векторных полей. Для продакшна с ростом данных закладывайте запас заранее, а не апгрейдьтесь по факту падения сервиса.

Что произойдёт, если памяти реально не хватит?

OOM-killer убьёт процесс Typesense, сервер (если настроен автоперезапуск) поднимется заново и начнёт восстанавливать индекс из данных на диске — это время простоя, пропорциональное объёму коллекции. Поэтому важнее не «что делать после», а не допускать ситуации через мониторинг и запас.

Нужен ли быстрый NVMe-диск, если данные всё равно живут в RAM?

Да. Диск отвечает за WAL и снапшоты — от его скорости зависит, насколько быстро сервер восстановится после рестарта и насколько устойчива запись при высокой нагрузке на индексацию.

Можно ли снизить память, просто уменьшив число реплик в кластере?

Память на нодy это не снизит — каждая нода Typesense-кластера хранит полную копию данных независимо от их числа. Уменьшение числа реплик снижает суммарные затраты на инфраструктуру и отказоустойчивость, но не требование к RAM одной ноды.

Стоит ли использовать один и тот же сервер под Typesense и под приложение (например, backend на Node.js)?

Для тестовой среды — можно, но в продакшне лучше разносить: Typesense съедает предсказуемый, но существенный объём RAM, и совместное размещение с приложением усложняет и мониторинг, и планирование апгрейдов.

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

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

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