MAATRIX / Блог / Сколько RAM нужно для Qdrant: расчёт по векторам

Сколько RAM нужно для Qdrant: расчёт по векторам

Сколько RAM нужно для Qdrant: расчёт по векторам

MAATRIX

Qdrant держит векторы и граф HNSW-индекса в оперативной памяти, и когда коллекция обгоняет расчёт, сервер падает по OOM без предупреждений — не «начинает тормозить», а просто исчезает из списка процессов. Готового ответа «столько-то гигабайт на миллион записей» не существует: всё зависит от размерности эмбеддингов, квантования и того, что лежит в payload. Ниже — формула, которой можно пользоваться как самим Qdrant, посчитанные примеры для популярных моделей и команды, чтобы проверить цифры на своём сервере.

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

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

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

Почему «сервер с запасом» — это не расчёт, а угадывание

Qdrant написан на Rust и по умолчанию держит и сами векторы, и граф HNSW-индекса в RAM — так поиск ближайших соседей отвечает за миллисекунды, а не секунды диска. Обратная сторона: как только выделенной памяти не хватает, ядро Linux останавливает процесс без деградации, а не замедляет его. В dmesg или journalctl -k это выглядит так:

kernel: Out of memory: Killed process 48213 (qdrant) total-vm:12581248kB, anon-rss:8321456kB, file-rss:0kB, shmem-rss:0kB, UID:998 pgtables:16820kB oom_score_adj:0

Секунду назад коллекция отвечала на запросы, следующим шагом сервис не поднимается или контейнер перезапускается пустым — данные на диске остаются, если volume смонтирован постоянно, но HNSW-индекс придётся строить заново. Обратная ошибка — взять сервер «на всякий случай» — тоже стоит денег: 32 ГБ RAM под коллекцию, которой на деле хватает 6 ГБ, это переплата каждый месяц без причины. Оба сценария лечатся одинаково: посчитать заранее, а не подбирать методом проб.

Память под Qdrant зависит от четырёх переменных: сколько у вас точек (points_count), какая размерность у эмбеддингов, храните ли вы полные float32-векторы или сжатую версию, и какой объём добавляют структуры HNSW-индекса поверх сырых данных. Формула в следующем разделе связывает все четыре.

Формула расчёта RAM: векторы × размерность × 4 байта × 1,5

Qdrant хранит каждую координату вектора как float32 — 4 байта. Базовый объём данных — это просто число точек, умноженное на размерность и на 4. Но сверху ложится граф HNSW: на нулевом уровне графа каждая точка хранит связи до 2×m соседей (по умолчанию m: 16, то есть до 32 связей), и каждая связь — это 4-байтовый идентификатор точки-соседа. Плюс структуры сегментов, ID-маппинг и служебные метаданные. Итоговое эмпирическое правило, которым удобно пользоваться как безопасной оценкой сверху:

RAM (байт) ≈ количество_точек × размерность × 4 × 1,5

Коэффициент 1,5 — не точная физика, а запас: он покрывает связи HNSW и накладные расходы сегмента и на практике оказывается близким к реальному потреблению для векторов средней и высокой размерности (от 384 и выше). Посчитать свой случай можно одной командой:

python3 -c "n=1_000_000; d=1536; print(round(n*d*4*1.5/1024**3, 2), 'ГБ')"

Для распространённых моделей эмбеддингов и размеров коллекций формула даёт такие цифры:

ВекторовРазмерностьПример моделиRAM по формуле
100 000384all-MiniLM-L6-v2, bge-small≈0,21 ГБ
100 0001536OpenAI text-embedding-3-small≈0,86 ГБ
1 000 000384all-MiniLM-L6-v2, bge-small≈2,15 ГБ
1 000 0001024Cohere embed-v3, bge-base≈5,72 ГБ
1 000 0001536OpenAI text-embedding-3-small≈8,58 ГБ
1 000 0003072OpenAI text-embedding-3-large≈17,17 ГБ
10 000 0001024Cohere embed-v3, bge-base≈57,22 ГБ

Это расчёт, а не замер конкретного стенда: реальная цифра на вашем сервере отклоняется на несколько процентов в зависимости от версии Qdrant, настроек сегментации и объёма payload рядом с векторами. Но как ориентир для выбора конфигурации сервера формула работает надёжно. Дальше — про параметры, которыми это число можно снизить; названия ключей ниже соответствуют актуальным версиям Qdrant линейки 1.x, для другой ветки сверяйтесь с документацией конкретного релиза.

Развернуть за пару минут

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

Развернуть Qdrant

Что формула не учитывает: payload, реплики и сегменты

Формула из раздела выше считает только сырые векторы и индекс поиска. У реальной коллекции есть ещё три статьи расхода, которые она не видит.

Payload. Каждая точка в Qdrant может нести произвольный JSON — заголовок документа, ссылку, теги, права доступа. Payload хранится отдельно от векторов (движок на основе RocksDB) и по умолчанию доступен для быстрой фильтрации — то есть тоже претендует на память. Если в payload лежит текст документа на пару килобайт, а не короткие поля, реальное потребление легко превышает расчёт по формуле в разы. Правило простое: тяжёлый контент храните в основной базе или объектном хранилище, а в Qdrant кладите только то, по чему фильтруете и что нужно вернуть в ответе. Параметр on_disk_payload: true при создании коллекции переносит payload на диск и почти полностью снимает эту статью расхода с RAM — ценой чуть более медленной фильтрации по полям.

Реплики. replication_factor в конфигурации коллекции — это буквально количество полных копий данных на разных узлах. Значение 2 означает, что весь расчёт из раздела выше нужно умножить на два — не потому что что-то экономится, а потому что каждая реплика хранит независимую полную копию сегментов ради отказоустойчивости.

Сегменты и шарды. Данные коллекции физически разбиты на сегменты (Qdrant сам решает, сколько их держать, ориентируясь на optimizers_config.default_segment_number), и на одном узле это не добавляет памяти сверх суммы данных — просто дробит её на части, которые оптимизируются и мержатся независимо. А вот shard_number при развёртывании на нескольких нодах распределяет сегменты между узлами: суммарная память по кластеру та же, но на каждом отдельном сервере оседает своя доля. Для одного сервера с одним узлом эта переменная на общий расчёт RAM не влияет — влияет только replication_factor.

Квантование: сжать float32 в int8 или в биты

Если по формуле выходит больше памяти, чем вы готовы держать, следующий шаг — не апгрейд сервера, а квантование: перевод координат вектора из float32 в более компактный тип. У Qdrant три режима, и они закрывают разные случаи, а не заменяют друг друга.

Scalar (int8). Каждая координата ужимается с 4 байт до 1 — четырёхкратное сокращение по определению типа данных, без исключений. Работает почти всегда без заметной просадки качества поиска и обычно это первый шаг, который стоит попробовать:

quantization_config:
  scalar:
    type: int8
    quantile: 0.99
    always_ram: true

Binary. Одна координата — один бит: сокращение в 32 раза против float32 (32 бита против 1). Qdrant рекомендует его для векторов размерностью от 1024 и выше — то есть именно для эмбеддингов OpenAI, Cohere и похожих моделей, у которых на такой размерности разделимость классов сохраняется даже после огрубления координаты до знака. На низкой размерности (та же 384) двоичное квантование теряет точность заметнее, и его стоит сначала проверить на своих запросах, а не включать вслепую.

Product (PQ). Компрессия настраивается явно — compression: x4/x8/x16/x32/x64 — и может ужать данные сильнее двоичного квантования, но за счёт декомпрессии на каждый запрос: дороже по CPU и заметнее теряет recall при агрессивных настройках.

quantization_config:
  product:
    compression: x16
    always_ram: true

Честный момент: конкретный процент потери точности зависит от вашей модели эмбеддингов и распределения данных — Qdrant прямо предупреждает об этом в собственной документации и не даёт универсальной цифры, поэтому не даём её и мы. Проверяется это не расчётом, а прогоном ваших реальных запросов через rescore — параметр поиска, который досчитывает top-k кандидатов по исходным векторам поверх быстрого квантованного прохода:

{
  "params": {
    "quantization": { "rescore": true, "oversampling": 2.0 }
  }
}

Для примера из таблицы выше — миллион векторов по 1536 измерений, ≈8,58 ГБ по формуле без квантования — двоичное квантование даёт 1 000 000 × 1536 / 8 = 192 000 000 байт, то есть около 183 МБ на сами квантованные векторы. Даже с запасом на служебные структуры графа и опциональное хранение оригиналов для rescore это на порядок меньше исходной оценки.

On-disk и mmap: когда диск важнее RAM

Держать всё в RAM — это выбор по умолчанию, а не обязательное требование. Если коллекция кратно больше доступной памяти или основная часть векторов запрашивается редко, Qdrant умеет работать через mmap — операционная система подгружает страницы данных с диска по требованию вместо того, чтобы держать всё резидентно.

Включается на уровне параметров вектора при создании коллекции:

{
  "vectors": { "size": 1536, "distance": "Cosine", "on_disk": true },
  "hnsw_config": { "on_disk": true }
}

Разница между двумя строчками важна. vectors.on_disk переводит на диск сами координаты — самую тяжёлую часть по формуле выше. hnsw_config.on_disk переводит на диск ещё и граф индекса; без него граф всё равно останется в RAM, даже если векторы уже на диске, — и для большинства нагрузок это правильный компромисс, потому что именно обход графа определяет скорость поиска, а не чтение одного вектора-кандидата. Полностью on-disk режим (оба параметра) экономит RAM максимально, но каждый шаг по графу может означать физическое чтение с диска — на NVMe с низкой задержкой случайного доступа это почти незаметно, на SATA-диске уже заметно. Разворачивать полностью on-disk индекс на медленном сетевом хранилище смысла обычно нет: выигрыш в цене RAM съедается временем ответа.

Есть и промежуточный, автоматический вариант — optimizers_config.memmap_threshold_kb: сегменты крупнее указанного порога Qdrant сам переводит в mmap-хранилище, не трогая мелкие свежие сегменты, которые ещё активно изменяются. Для коллекции, растущей постепенно, это удобнее ручного on_disk: true с первого дня — память освобождается по мере роста, а не резервируется наперёд.

Как проверить фактическое потребление, а не только посчитать

Формула — это оценка для выбора сервера, а не замена контроля после запуска. Три источника показывают реальное потребление, а не расчётное.

Память процесса на уровне ОС — самый честный источник, он не зависит от того, что Qdrant считает нужным сообщить:

docker stats qdrant --no-stream
# или без контейнера:
cat /proc/$(pgrep -f qdrant)/status | grep -E 'VmRSS|VmSwap'

VmRSS — физически занятая память прямо сейчас, VmSwap в норме должен быть нулевым или близким к нулю: если Qdrant активно уходит в своп, фактическая нагрузка уже обогнала RAM, и дело не дойдёт до штатного OOM — просто резко просядет задержка ответа ещё до того, как ядро решит убить процесс.

Состояние коллекции изнутри — через REST API, без захода в контейнер:

curl -s http://localhost:6333/collections/my_collection \
  | jq '.result | {points_count, segments_count, indexed_vectors_count, status}'

Если indexed_vectors_count заметно отстаёт от points_count — HNSW-индекс ещё достраивается (обычно после массовой заливки), и пиковое потребление памяти в этот момент выше, чем после завершения индексации: построение графа временно требует памяти сверх готовой структуры.

Если процесс упирается в лимит, а системный overcommit настроен нестандартно, вместо мгновенного OOM-килла можно увидеть панику самого процесса — Rust отдельно обрабатывает неудачную аллокацию:

memory allocation of 1048576 bytes failed

и процесс завершается по SIGABRT, а не зависает молча. В большинстве конфигураций Linux сработает именно OOM killer из раздела выше, но если такая строка встретилась в логе Qdrant — причина та же: памяти не хватило на конкретную операцию. Читайте её как сигнал увеличить RAM или включить квантование, а не как баг в самом Qdrant.

Какой сервер под Qdrant брать в MAATRIX

Qdrant — не прокси и не веб-сервер: нагрузка ровно та, что в разделах выше, — RAM под векторы и индекс, плюс диск, если часть данных вынесена в on-disk режим. Процессор нужен на индексацию (построение графа HNSW при вставке) и на rescore при квантовании, но именно память определяет, влезет коллекция или нет.

Минимум: 2 vCPU, 4 ГБ RAM, 40 ГБ NVMe. По формуле выше это ≈450 тысяч векторов размерностью 1536 без квантования — но это верхняя граница по чистому расчёту, без вычета памяти под операционную систему и сам процесс Qdrant в простое. Честное ограничение: реальный запас стоит планировать процентов на 30 меньше этой цифры, а при массовой заливке данных на пике легко упереться в потолок ещё до того, как коллекция окончательно заполнится — временный расход памяти на построение графа выше, чем у готового индекса.

Комфортный вариант: 4 vCPU, 16 ГБ RAM, 80–160 ГБ NVMe. По формуле это ≈1,86 млн векторов той же размерности; с учётом запаса под payload, репликацию и разовые пики при индексации комфортно рассчитывать на 1–1,3 млн без квантования, либо на порядок больше — с binary-квантованием и rescore с диска. Дополнительное ядро закрывает параллельную индексацию при заливке, не роняя скорость обслуживания текущих запросов. Если коллекция растёт на миллионы записей в месяц, закладывайтесь на 32 ГБ сразу: дешевле один раз взять сервер с запасом, чем переносить данные под нагрузкой.

Локация — Великобритания. Векторная база почти всегда стоит рядом с приложением или сервисом эмбеддингов, а не сама по себе, и для неё важны две вещи: низкая задержка до остальной инфраструктуры в Европе и понятный юридический режим для данных, из которых эти векторы посчитаны (UK GDPR действует независимо от Brexit и по требованиям близок к европейскому регламенту). Если приложение и пользователи — в ЕС или Великобритании, лондонский узел держит RTT до них в единицах-десятках миллисекунд, а не через океан.

Приложение Qdrant ставится автоматически при заказе сервера — из каталога apps.maatrix.io, без ручной установки. Доступы (адрес панели и порт REST API) появляются в личном кабинете, в разделе «Доступ», сразу после разворачивания. Оплата — картой российского банка, по СБП или криптовалютой: сервер в Лондоне не требует зарубежной карты или посредников.

Развернуть за пару минут

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

Развернуть Qdrant

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

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

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

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

Хватит ли 2 ГБ RAM, чтобы просто попробовать Qdrant?

Да, для теста на тысячах — первых десятках тысяч векторов небольшой размерности процесс стартует и работает в 2 ГБ, сам Qdrant в простое занимает заметно меньше сотни мегабайт. Но это тестовый, а не рабочий объём: по формуле из раздела выше миллион векторов даже на 384 измерениях уже съедает больше 2 ГБ, и вплотную к границе вы упрётесь в OOM без предупреждения.

Что будет, если превысить RAM без квантования и без on-disk режима?

Ядро Linux остановит процесс через OOM killer. Данные на диске сохранятся, если volume смонтирован постоянно, но HNSW-индекс придётся достраивать заново после перезапуска. Регулярно проверяйте VmRSS и переходите на квантование или on_disk: true заранее, а не после первого падения.

Нужно ли увеличивать RAM при подключении реплик?

Да, линейно. replication_factor: 2 означает две полные независимые копии данных на разных узлах — берите базовую формулу и умножайте на число реплик, скидок за то, что данные «те же самые», Qdrant не делает.

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

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