Прямой ответ
Выбор векторной базы в 80% случаев решается одним вопросом: есть ли уже Postgres? Если да — начните с pgvector: транзакции, бэкапы, JOIN с бизнес-данными и полнотекстовый поиск в одной базе, эксплуатационные знания те же (Postgres-база); для объёмов до миллионов векторов и умеренного QPS этого достаточно, а HNSW-индекс закрывает типовую скорость. Qdrant — когда векторный поиск становится отдельной высоконагруженной подсистемой: миллиарды векторов, сложный payload-фильтринг на высокой скорости, горизонтальное масштабирование, распределённый кластер. Chroma/FAISS — прототипы и встраивание (файловое/in-process хранилище, ноль инфраструктуры — RAG-старт). Milvus — кластерная система под экстремальные объёмы с богатой экосистемой, ценой сложности эксплуатации. Ошибка выбора обычно не «не та база», а преждевременная отдельная база: отдельный сервер векторов вместо таблицы в Postgres добавляет бэкапы, мониторинг, консистентность (вектор и запись разошлись) — без выигрыша, пока нагрузка мала.
pgvector: векторы в существующем Postgres
Расширение CREATE EXTENSION vector — и таблица: embedding vector(768), индекс CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops) — готовый ANN-поиск (ORDER BY embedding <=> $1 LIMIT 10). Сильные стороны: одна система (вектор обновляется в той же транзакции, что бизнес-запись — консистентность бесплатна; бэкап покрывает всё — стратегия); гибридный поиск из коробки (векторный ORDER BY + WHERE-фильтры + полнотекстовый tsvector + RRF-слияние — один SQL); известные инструменты (psql, pg_dump, репликация, пулинг). Ограничения: HNSW-индекс живёт в памяти (миллионы 768-мерных векторов — единицы ГБ RAM на индекс); фильтрация «после ANN» может терять релевантность при узких фильтрах (iterative scan в новых версиях улучшает); QPS в тысячи на тяжёлых запросах — потолок наступает раньше, чем у специализированных движков. Практический вывод: до ~5–10 млн векторов и сотен QPS pgvector — разумный дефолт; «выросли» — миграция в Qdrant отдельным проектом, не авансом.
Qdrant: специализация и масштаб
Rust-движок, спроектированный под векторный поиск: payload-индексы (фильтрация по метаданным строится в индексе и применяется эффективно — «документы отдела X за 2026 год» на миллиардной коллекции); квантизация (scalar/product/binary — сжатие векторов в разы с малой потерей точности: RAM-аппетит больших коллекций — главная статья расходов); распределённость (шардирование и репликации из коробки — кластер на три узла в compose-стеке поднимается за час); богатый API (gRPC/REST, снапшоты, alias'ы коллекций для zero-downtime переиндексации). Цена: отдельная система — свой бэкап/мониторинг/обновления; транзакционности с бизнес-БД нет (двойная запись «Postgres + Qdrant» требует саги/outbox-паттерна — рассинхрон возможен и должен обрабатываться). Когда оправдан: коллекция >10 млн векторов, сложные фильтры под нагрузкой, отдельная команда/сервис поиска, требование кластеризации. Milvus — тот же класс с акцентом на экстремальный масштаб (миллиарды, кластеры из десятков узлов) и ценой более тяжёлой архитектуры (несколько компонентов, объектное хранилище под данные); оправдан в платформах, где поиск — центральная инфраструктура.
Chroma и лёгкие варианты: прототип без инфраструктуры
Chroma — встраиваемая (in-process) или лёгкая серверная база: pip install chromadb, коллекция в локальной папке — ноль DevOps для старта; API простой (add/query с метаданными), для прототипа RAG — оптимальна; потолок — производительность и надёжность прод-нагрузки (это инструмент разработчика, не прод-хранилище — миграция на pgvector/Qdrant при продуктовлении закладывается сразу: слой доступа к индексу — тонкий интерфейс, не размазанный по коду). FAISS — библиотека (не база): быстрые индексы в памяти процесса, без персистентности/фильтрации/сервера — для пайплайнов и экспериментов, где «база» не нужна. LanceDB/SQLite-vec — файловые встраиваемые варианты того же класса. Правило перехода: прототип на Chroma → продукт на pgvector (если есть Postgres) или Qdrant (если поиск — отдельная подсистема); прямой путь «сразу Milvus» без миллиардных объёмов — переплата сложностью.
Критерии выбора одной таблицей
Сводить критерии удобно в матрицу решения: объём коллекции (до 100k — Chroma/FAISS; до 5–10M — pgvector; выше — Qdrant/Milvus); наличие Postgres в стеке (есть — сильный аргумент за pgvector: одна система, одна эксплуатация); характер запросов (гибрид «вектор + реляционные JOIN + полнотекст» — Postgres выигрывает; чистый векторный поиск с тяжёлой фильтрацией под нагрузкой — Qdrant); требования к кластеризации и шардированию (нужны — Qdrant/Milvus); зрелость команды (специализированная база — новая зона ответственности: снапшоты, обновление, мониторинг — их нужно кому-то вести). Решение принимается по текущим критериям с записанными триггерами пересмотра, а не «на вырост»: преждевременная распределённая система обходится дороже будущей миграции.
Практический сценарий: эволюция поиска в продукте
Этап 1 (MVP, 200 документов): Chroma в процессе FastAPI-приложения — поиск по документации работает, инфраструктура нулевая. Этап 2 (продукт, 50k документов, Postgres уже есть): миграция на pgvector — таблица doc_chunks (текст, метаданные, embedding), HNSW-индекс; выигрываем транзакционность (новый документ — одна транзакция), гибридный поиск (вектор + tsvector), бэкапы; слой доступа переписан за день благодаря тонкому интерфейсу. Этап 3 (рост, 2M чанков, фильтрация по тенантам под нагрузкой): запрос с фильтром tenant_id стал тормозить (фильтр узкий, ANN даёт много отсеиваемого) — пробуем iterative scan и частичные индексы по крупным тенантам — хватает; решение «мигрировать на Qdrant» отложено с фиксацией триггеров (QPS > 500, коллекция > 10M, потребность в квантизации). Контрветка сценария: у другой команды поиск — отдельный микросервис на миллиардах товарных векторов с десятками фильтров — Qdrant-кластер сразу, без этапа pgvector: критерии разные, «правильная база» зависит от нагрузки, а не от моды. Оба решения задокументированы с триггерами пересмотра — выбор живёт, пока верны предпосылки.
FAQ
Как жить с двумя системами (Postgres + Qdrant) без рассинхрона? Паттерн outbox: бизнес-запись и задача «обновить вектор» пишутся в одной транзакции Postgres, отдельный воркер применяет задачу к Qdrang с ретраями — рассинхрон возможен только временно и самовосстанавливается; периодическая сверка (пересчёт хешей документов против индекса) ловит остаточные расхождения. Прямая двойная запись «в двух местах сразу» без outbox — источник тихих багов.
Можно ли вообще без векторной базы — в numpy? До десятков тысяч векторов — да: матрица эмбеддингов в памяти + косинус через numpy считает перебор за миллисекунды, точнее любого ANN; «база» нужна для персистентности, фильтрации и масштаба — не раньше.
Что с точностью ANN против полного перебора? HNSW-индексы дают recall ~95–99% при настройках по умолчанию (параметры ef_search/m — торговля скорость/точность); для критичных к полноте сценариев — точный перебор (pgvector без индекса) на отфильтрованном подмножестве — часто приемлемо по скорости.
Как мигрировать между базами? Векторы пересчитывать не нужно (та же модель — те же векторы): экспорт/импорт коллекций скриптом; переключение через alias/фича-флаг с параллельной работой (двойная запись на время сверки); переиндексация обязательна только при смене эмбеддинг-модели.
Бэкапить ли векторные данные? Да, как любые: pgvector — штатный бэкап Postgres; Qdrant — снапшоты коллекций; но cheaper-путь при наличии исходников (документы + модель) — восстановить индекс переиндексацией: бэкап эмбеддингов часто избыточен, если переиндексация занимает часы, а не недели.
Источники: документация Qdrant, pgvector, Milvus — ссылки в шапке.