Сколько RAM нужно для Qdrant: расчёт по векторам
Qdrant держит векторы и граф HNSW-индекса в оперативной памяти, и когда коллекция обгоняет расчёт, сервер падает по OOM без предупреждений — не «начинает тормозить», а просто исчезает из списка процессов. Готового ответа «столько-то гигабайт на миллион записей» не существует: всё зависит от размерности эмбеддингов, квантования и того, что лежит в payload. Ниже — формула, которой можно пользоваться как самим Qdrant, посчитанные примеры для популярных моделей и команды, чтобы проверить цифры на своём сервере.
Содержание
- Почему «сервер с запасом» — это не расчёт, а угадывание
- Формула расчёта RAM: векторы × размерность × 4 байта × 1,5
- Что формула не учитывает: payload, реплики и сегменты
- Квантование: сжать float32 в int8 или в биты
- On-disk и mmap: когда диск важнее RAM
- Как проверить фактическое потребление, а не только посчитать
- Какой сервер под Qdrant брать в MAATRIX
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество 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 000 | 384 | all-MiniLM-L6-v2, bge-small | ≈0,21 ГБ |
| 100 000 | 1536 | OpenAI text-embedding-3-small | ≈0,86 ГБ |
| 1 000 000 | 384 | all-MiniLM-L6-v2, bge-small | ≈2,15 ГБ |
| 1 000 000 | 1024 | Cohere embed-v3, bge-base | ≈5,72 ГБ |
| 1 000 000 | 1536 | OpenAI text-embedding-3-small | ≈8,58 ГБ |
| 1 000 000 | 3072 | OpenAI text-embedding-3-large | ≈17,17 ГБ |
| 10 000 000 | 1024 | Cohere 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 — десятки моделей в одном окне. Оплата картой РФ и по СБП.