Почему векторная база внезапно просит много памяти

На демонстрации локального RAG всё выглядит скромно: несколько тысяч документов, один сервер и быстрый поиск. Затем база получает историю договоров, инструкции, переписку, несколько версий каждого файла и разграничение доступа. Число фрагментов растёт до миллионов, и в смете неожиданно появляется дорогая оперативная память.

Причина проста: вектор — это массив чисел. Для эмбеддинга размерности 768 в формате float32 нужно 768 × 4 = 3072 байта, то есть около 3 КБ. Миллион таких векторов занимает примерно 3,07 ГБ только в сыром виде. При размерности 1536 объём удваивается до 6,14 ГБ.

Но это не весь индекс. К векторам добавляются граф поиска HNSW или структуры IVF, идентификаторы, payload, служебные данные, временная память для построения индекса, кэш, реплика и запас для роста. Поэтому умножить число векторов на размерность — полезный первый шаг, но опасный финальный бюджет.

Из чего складывается реальное потребление

Для локального RAG память нужно считать по слоям:

  • исходные векторы или их сжатое представление;
  • структура приблизительного поиска;
  • метаданные и фильтры доступа;
  • полнотекстовый или разреженный индекс, если поиск гибридный;
  • кэш горячих данных;
  • резерв на построение и обновление индекса;
  • реплика для отказоустойчивости;
  • запас 20–30% на рост и пики.

HNSW строит граф связей между близкими векторами и обычно даёт хорошую задержку и полноту поиска, но граф тоже занимает память. Его объём зависит от параметра связности, реализации и числа объектов. IVF разбивает пространство на кластеры и может снизить объём активного поиска, однако требует настройки количества кластеров и числа просматриваемых ячеек.

Документация Milvus прямо описывает семейства FLAT, IVF, HNSW и их сжатые варианты. Qdrant и Weaviate отдельно документируют перенос части данных на диск и квантование. Общий вывод одинаков: индекс выбирают не по таблице «быстрее — медленнее», а по сочетанию объёма, задержки, фильтров, обновлений и допустимой потери качества.

Что даёт квантование

Квантование хранит вектор с меньшей точностью. Вместо 32-битного числа на координату можно использовать 8 бит, двоичное представление или код сегмента.

Скалярное 8-битное квантование теоретически сокращает представление float32 примерно в четыре раза. Для миллиона векторов размерности 768 сырые векторы уменьшаются примерно с 3,07 до 0,77 ГБ. Qdrant указывает, что scalar quantization также может ускорять поиск благодаря более компактным данным и эффективным инструкциям процессора.

Product quantization делит вектор на сегменты и хранит для каждого сегмента идентификатор ближайшего центра. В примере Weaviate вектор из 768 float32-координат занимает 3072 байта, а после PQ с 128 сегментами — около 128 байт без небольшого служебного объёма. Это почти 24-кратное уменьшение именно представления вектора.

Однако слово «почти» здесь важнее числа 24. В полном контуре остаются граф, метаданные и часто оригинальные векторы на диске для повторного ранжирования. Квантование с потерями также может изменить порядок близких кандидатов.

Почему нельзя экономить только по гигабайтам

Для RAG важна не абстрактная точность расстояний, а то, попал ли нужный фрагмент в набор, переданный модели. Если квантование сэкономило сервер, но снизило recall@k на редких договорах или русскоязычных формулировках, генератор не сможет восстановить отсутствующее доказательство.

Поэтому сравнивают минимум четыре режима на собственном корпусе:

  • полный float32-индекс;
  • 8-битное scalar quantization;
  • более агрессивное PQ или binary quantization;
  • сжатый первичный поиск с overfetch и повторным ранжированием оригинальными векторами.

В каждом режиме измеряют:

  • recall@k или долю запросов, где эталонный фрагмент попал в top-k;
  • качество ответа после генерации, а не только поиска;
  • p50 и p95 задержки;
  • фактическую память процесса под рабочей нагрузкой;
  • время построения и обновления индекса;
  • число обращений к диску;
  • стоимость серверов и резервной копии.

Weaviate описывает rescoring: система сначала получает кандидатов по сжатым векторам, затем пересчитывает расстояние по оригиналам. Это способ вернуть часть качества, заплатив диском, дополнительными вычислениями и небольшим увеличением задержки. Чудес не происходит; просто счёт переносится в другую строку.

Модельный расчёт для пяти миллионов фрагментов

Рассмотрим локальный RAG с пятью миллионами векторов размерности 768. Допущения:

  • исходный формат — float32;
  • один вектор соответствует одному фрагменту;
  • средний payload с идентификаторами, ACL и служебными полями — 500 байт;
  • одна рабочая реплика и одна резервная копия;
  • запас по памяти — 25%;
  • стоимость серверов зависит от конкретной площадки и в пример не включена.

Сырые векторы занимают около 15,36 ГБ. Payload добавляет примерно 2,5 ГБ. До учёта графа, кэша и служебных структур рабочий набор уже близок к 17,9 ГБ. С запасом получается 22,4 ГБ, а HNSW может поднять требование ещё выше.

При 8-битном scalar quantization сами векторы уменьшаются примерно до 3,84 ГБ. Payload не меняется, поэтому базовый набор — около 6,34 ГБ, или 7,9 ГБ с резервом до графа. Экономия полного процесса будет меньше четырёх раз: несжимаемые структуры никуда не исчезают.

При PQ размер векторной части может стать ещё меньше, но итог нужно проверять на реальном индексе. Если для rescoring сохраняются оригиналы на SSD, бюджет RAM уменьшается, а объём диска остаётся. Репликация удваивает рабочее хранение, резервные копии и синие индексы для безопасного обновления временно увеличивают его ещё сильнее.

Как считать экономику, а не только инфраструктуру

Полезная единица — стоимость поиска, который привёл к принятому ответу. В числитель входят:

  • амортизация RAM и CPU узлов;
  • NVMe или сетевое хранилище;
  • репликация и резервные копии;
  • построение индекса и переиндексация;
  • мониторинг, администрирование и обновления;
  • цена ошибок поиска и ручной проверки.

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

Для малого бизнеса особенно важна загрузка. Сервер с 256 ГБ RAM может выглядеть надёжным запасом, но при небольшом корпусе и десяти запросах в минуту он превращается в дорогой предмет интерьера. Иногда выгоднее хранить оригиналы на NVMe, держать сжатый индекс в памяти и принимать дополнительные 10–20 мс задержки.

Когда какой подход уместен

Полный HNSW без сжатия подходит, если корпус помещается в доступную RAM, задержка критична, а качество поиска дорого терять. Он удобен как эталон для сравнения.

Scalar quantization — разумный первый кандидат, когда память стала ограничением, но хочется сохранить предсказуемое качество. Его проще тестировать и объяснять, чем более агрессивные схемы.

PQ и binary quantization полезны при больших коллекциях и жёстком лимите RAM, особенно вместе с overfetch и rescoring. Они требуют внимательнее проверять редкие классы запросов.

Дисковый или memory-mapped индекс подходит, когда объём важнее минимальной задержки и есть быстрый локальный NVMe. Для нерегулярной внутренней базы знаний это часто лучше, чем покупка дополнительной памяти.

FLAT-поиск уместен для небольших коллекций, фильтрованных подмножеств и контрольного эталона. На малом корпусе сложный индекс может стоить дороже, чем полный перебор.

Пилот на одной копии корпуса

Практический тест занимает несколько дней:

1. Зафиксируйте текущий корпус, модель эмбеддингов и 100–300 реальных запросов с эталонными фрагментами.
2. Соберите базовый float32-индекс и измерьте качество, память, p95 и время построения.
3. Повторите тест с 8-битным квантованием.
4. Если экономии недостаточно, добавьте PQ или binary-режим с overfetch и rescoring.
5. Проверьте фильтры доступа: ускорение не должно возвращать запрещённые документы.
6. Отдельно измерьте массовое обновление и безопасное переключение между версиями индекса.
7. Посчитайте стоимость принятого ответа и выберите минимальную конфигурацию, проходящую пороги качества и SLA.

Главная управленческая мысль проста: покупать RAM до измерений не нужно. Сначала посчитайте число векторов и несжимаемый служебный объём, затем подтвердите на своём корпусе, сколько качества можно обменять на меньший индекс. У векторной базы нет аппетита как такового — только архитектура, которую кто-то не посчитал заранее.