Векторные базы данных: Qdrant, Chroma, Pinecone — сравнение для AI-разработчика

AI и LLM
Схема выбора векторного хранилища: от pgvector и FAISS до Qdrant, Chroma и Pinecone для RAG-приложений

TL;DR: Прежде чем ставить отдельную векторную БД, проверьте, не хватает ли вам pgvector в существующем PostgreSQL или FAISS/hnswlib в памяти при перестроении индекса по расписанию. Если специализированный движок всё же нужен: Chroma — для прототипов и embedded-сценариев, Qdrant — для продакшена, где важны фильтрация по метаданным и контроль над данными, Pinecone — когда вы сознательно покупаете отсутствие DevOps и готовы платить за managed-сервис.

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

Нужна ли вообще отдельная векторная БД

Честный ответ: не всегда. Специализированный движок — это отдельный сервис, который надо разворачивать, мониторить, бэкапить и обновлять. Есть два сценария, где без него можно обойтись.

Сценарий 1: pgvector в существующем PostgreSQL

Если у вас уже есть PostgreSQL и эмбеддингов немного (порядок сотен тысяч — единиц миллионов), расширение pgvector закрывает большинство задач. Оно поддерживает два ANN-индекса:

  • HNSW — более точный и быстрый на поиске, но требует больше памяти и дольше строится.
  • IVFFlat — строится быстрее, легче, но требует предварительного обучения индекса (lists) и уступает HNSW по recall.

Ограничения, о которых стоит знать до принятия решения: тип vector индексируется до 2000 измерений, тип halfvec (half-precision) — до 4000. Для большинства open-source эмбеддинг-моделей (768–1536 измерений) это не проблема. Фильтрация по метаданным выполняется обычными SQL-условиями — но ANN-поиск и фильтр комбинируются не всегда оптимально, при сильно селективных фильтрах recall проседает, и это приходится компенсировать настройками.

Главный аргумент за pgvector — не скорость, а отсутствие нового компонента: векторы живут рядом с данными, транзакции и бэкапы уже решены, джойны с бизнес-таблицами бесплатны.

Сценарий 2: FAISS или hnswlib в памяти

Если корпус документов статичен или меняется редко, а чтений много, вариант «FAISS/hnswlib в памяти процесса + перестроение индекса по расписанию» часто рациональнее любой БД. Нет сетевого хопа на поиске, нет сервиса, который надо поддерживать, индекс — это файл, который версионируется и выкатывается вместе с релизом. Типовые грабли: индекс нельзя обновлять инкрементально без пересборки (у FAISS есть IndexIVF с добавлением, но удаление записей всё равно неудобное), и при нескольких репликах сервиса каждая держит индекс в памяти. Подробнее про устройство таких контуров памяти я писал в статье про память AI-систем.

Когда отдельный движок оправдан

Специализированная векторная БД оправдана, когда выполняется хотя бы пара условий:

  • Данные пишутся и обновляются часто (реалтайм-индексация новых документов).
  • Запросы идут от нескольких независимых сервисов — нужен сетевой доступ с аутентификацией.
  • Поиск должен комбинировать близость векторов со сложной фильтрацией по метаданным.
  • Объём не помещается в память одного узла или требуется репликация.

Если это ваш случай — дальше выбираем между тремя вариантами.

Qdrant: контроль и фильтрация

Qdrant — open-source векторная БД, лицензия Apache 2.0, доступна в self-hosted варианте и как Qdrant Cloud. Вокруг неё удобно строить полноценный RAG-пайплайн для корпоративных документов, потому что фильтры и индексы настраиваются явно.

Ключевые возможности

  • Индекс HNSW с настраиваемыми параметрами графа (m, ef_construct), опциональное сжатие векторов: scalar quantization и product quantization плюс поддержка float16. На больших коллекциях квантизация даёт заметную экономию RAM при приемлемой потере recall — проверяйте на своих данных, а не на чужих бенчмарках.
  • Фильтрация по payload: условия must, should, must_not, диапазоны, гео, вложенные объекты, полнотекстовый индекс по текстовым полям. Для Qdrant фильтр — первоклассная часть запроса, а не постобработка.
  • Гибридный и разреженный поиск: поддержка sparse-векторов (BM25-подобные представления) и гибридных запросов со слиянием результатов.
  • Кластерность: шардирование и репликация коллекций, снапшоты для бэкапов.

Когда выбирать Qdrant

  • Данные и инфраструктура должны оставаться под вашим контролем (on-prem, свой VPC).
  • Запросы активно фильтруются по метаданным — например, по тенанту, языку, дате документа.
  • Нужна возможность перейти из self-hosted в облако без смены API.

Пример (актуальный qdrant-client, Python)

from qdrant_client import QdrantClient
from qdrant_client.models import (
    Distance, VectorParams, PointStruct,
    Filter, FieldCondition, MatchValue,
)

client = QdrantClient(url="http://localhost:6333")
# В облаке: QdrantClient(url="https://<cluster>.qdrant.io", api_key="...")

COLLECTION = "docs"
if not client.collection_exists(COLLECTION):
    client.create_collection(
        collection_name=COLLECTION,
        vectors_config=VectorParams(size=1024, distance=Distance.COSINE),
    )

client.upsert(
    collection_name=COLLECTION,
    points=[
        PointStruct(
            id=1,
            vector=[0.1, 0.2, 0.3],  # ваш эмбеддинг
            payload={"text": "Политика возвратов", "category": "policy", "lang": "ru"},
        ),
    ],
)

result = client.query_points(
    collection_name=COLLECTION,
    query=[0.15, 0.25, 0.35],  # эмбеддинг запроса
    limit=3,
    query_filter=Filter(
        must=[FieldCondition(key="lang", match=MatchValue(value="ru"))]
    ),
)
for point in result.points:
    print(point.score, point.payload["text"])

Обратите внимание: поиск в актуальном клиенте — это query_points, а не старый search. Старый метод оставлен для обратной совместимости, новый код пишите на query_points.

Chroma: минимальный порог входа

Chroma — open-source векторная БД (Apache 2.0), изначально ориентированная на прототипирование RAG прямо из Python-кода. В версиях 1.x ядро переписано на Rust, помимо embedded-режима есть серверный (chroma run) и управляемый Chroma Cloud, так что «Chroma — это только in-process на Python» уже неактуальная характеристика. Есть и JavaScript-клиент.

Ключевые возможности

  • Быстрый старт: одна строка — и у вас персистентная локальная БД в каталоге проекта.
  • Embedded и клиент-сервер: можно начать in-process, а затем поднять тот же код против chroma run --path ./chroma_db без переписывания логики.
  • Индекс HNSW с выбором метрики расстояния через метаданные коллекции (hnsw:space: cosine, l2, ip).
  • Фильтрация по метаданным операторами $eq, $ne, $gt, $in, $and, $or — для прототипа достаточно, хотя выразительность уступает Qdrant.
  • Встроенные эмбеддинг-функции: коллекция умеет сама вызывать провайдера эмбеддингов, если передать конфигурацию при создании.

Ограничения, которые стоит знать

Chroma сознательно не даёт низкоуровневой настройки HNSW (m, ef_construct), нет родного шардирования коллекции на несколько узлов в open-source. Для нагруженных продакшен-систем с десятками миллионов векторов это потолок — либо вертикальное масштабирование одного узла, либо Chroma Cloud.

Когда выбирать Chroma

  • Прототипы, MVP, демо — туда, где скорость разработки важнее тюнинга.
  • Embedded-сценарии: десктопные приложения, CLI-утилиты, тесты.
  • Команда без DevOps, которой пока хватает одного узла. Кстати, про бюджетную сторону таких решений — в разборе реальных бюджетов на AI-MVP.

Пример (актуальный chromadb, Python)

import chromadb

# Embedded + персистентность на диске
client = chromadb.PersistentClient(path="./chroma_db")
# Против сервера: chromadb.HttpClient(host="localhost", port=8000)

collection = client.get_or_create_collection(
    name="docs",
    metadata={"hnsw:space": "cosine"},
)

collection.add(
    ids=["doc1", "doc2"],
    embeddings=[[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]],
    documents=["Политика возвратов", "Регламент отпусков"],
    metadatas=[{"category": "policy"}, {"category": "hr"}],
)

results = collection.query(
    query_embeddings=[[0.15, 0.25, 0.35]],
    n_results=2,
    where={"category": {"$eq": "hr"}},
)
print(results["ids"], results["distances"])

Pinecone: покупка отсутствия DevOps

Pinecone — закрытый, полностью управляемый облачный сервис. Кода и языка реализации у него нет в публичном доступе; вы работаете только с API. Всё администрирование — индексы, реплики, обновления, бэкапы — на стороне провайдера.

Ключевые возможности

  • Serverless-индексы: хранение и вычисления разделены, данные лежат в объектном хранилище провайдера, оплата зависит от потребления. Подовые (pod-based) индексы остаются как legacy-опция; для новых проектов берите serverless-архитектуру.
  • Автомасштабирование под нагрузку и рост коллекции без ручных операций.
  • Фильтрация по метаданным с операторами $eq, $in, $lt и т.д. Напоминание: из-за архитектуры индекса сильно селективные фильтры могут влиять на recall — это ограничение любого ANN с post-filtering, не специфика Pinecone.
  • Namespace для логического разделения данных внутри одного индекса (удобно для мультитенантности).
  • Интеграции с основными LLM-фреймворками. Если выбираете между ними, взгляните на сравнение LangChain vs LlamaIndex.

Ограничения

Производительность и поведение индекса — чёрный ящик. Низкоуровневого тюнинга ANN нет. Vendor lock-in присутствует: миграция означает выгрузку векторов и метаданных целиком.

Когда выбирать Pinecone

  • Требуется вывести в продакшен систему с жёсткими требованиями к доступности, а DevOps-компетенции в команде нет и нанимать её не планируется.
  • Нагрузка непредсказуема, и автомасштабирование важнее контроля над тюнингом индекса.
  • Регуляторные требования допускают хранение векторов и метаданных у внешнего провайдера.

Пример (актуальный pinecone SDK, Python)

from pinecone import Pinecone, ServerlessSpec

pc = Pinecone(api_key="YOUR_API_KEY")

INDEX = "docs"
DIM = 1024

if not pc.has_index(INDEX):
    pc.create_index(
        name=INDEX,
        dimension=DIM,
        metric="cosine",
        spec=ServerlessSpec(cloud="aws", region="us-east-1"),
    )

index = pc.Index(INDEX)

index.upsert(
    vectors=[
        {"id": "doc1", "values": [0.1, 0.2, 0.3],
         "metadata": {"category": "policy", "lang": "ru"}},
    ],
    namespace="kb",
)

results = index.query(
    vector=[0.15, 0.25, 0.35],
    top_k=3,
    namespace="kb",
    filter={"lang": {"$eq": "ru"}},
    include_metadata=True,
)
for match in results["matches"]:
    print(match["score"], match["metadata"])

Старый стиль pinecone.init() и pinecone.Index(...) как глобальные функции давно удалён из SDK — актуальный интерфейс только через объект Pinecone, как в примере выше.

Сравнительная таблица

КритерийQdrantChromaPinecone
ЛицензияApache 2.0Apache 2.0Проприетарный сервис
Форма поставкиSelf-hosted бинарь/Docker, Qdrant CloudEmbedded-библиотека, сервер (chroma run), Chroma CloudТолько managed cloud
ИндексHNSW (настраиваемый m, ef_construct), sparse-векторыHNSW (метрика через metadata коллекции)Управляемый провайдером, детали закрыты
Фильтрация по метаданнымmust/should/must_not, диапазоны, гео, полнотекст$eq, $in, $gt, $and, $or$eq, $in, $lt и др., плюс namespace
КвантизацияScalar, product, float16НетУправляется провайдером
Что администрируетеУзлы, диски, снапшоты, обновления (self-hosted)Процесс сервера и диск; в embedded — ничегоНичего
Модель оплатыБесплатно self-hosted; облако — по ресурсамБесплатно open-source; облако — по потреблениюПо потреблению (serverless)

Моя рекомендация

Считайте сверху вниз и остановитесь на первом подходящем уровне:

  1. Уже есть PostgreSQL и эмбеддингов до единиц миллионов? Ставьте pgvector с HNSW. Не приносите новый сервис, пока его отсутствие не стало измеримой проблемой.
  2. Корпус статичен, чтений много? FAISS или hnswlib в памяти с перестроением индекса по расписанию. Дешевле и надёжнее любой БД.
  3. Прототип или embedded-приложение? Chroma. Переход на серверный режим — вопрос смены клиента, а не архитектуры.
  4. Продакшен под вашим контролем, богатая фильтрация, частые записи? Qdrant. Это мой вариант по умолчанию для корпоративных RAG: предсказуемое поведение, честная настройка индекса, отсутствие vendor lock-in.
  5. Жёсткий SLA, нет и не будет DevOps? Pinecone. Вы платите деньгами за то, чтобы не платить временем и риском эксплуатации.

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

FAQ

Можно ли использовать несколько векторных БД в одном проекте?

Можно, но внутри одного кейса это редко оправдано. Нормальная практика — разные инструменты на разных стадиях: Chroma локально у разработчиков, Qdrant или pgvector в продакшене. Держать два движка для одной и той же задачи — двойная стоимость синхронизации и расхождение поведения индексов.

Что быстрее: Qdrant, Chroma или Pinecone?

Универсального ответа нет: latency зависит от объёма коллекции, параметров HNSW, селективности фильтров и железа. Честный способ выбрать — поднять пилот на своих данных и своих запросах и измерить. Чужие бенчмарки используйте только для отсева явных неподходящих вариантов, а не для финального решения.

Qdrant или pgvector?

pgvector — если векторы по объёму и QPS укладываются в ваш PostgreSQL и фильтрация простая. Qdrant — когда появляются сложные условия по метаданным, частые записи, потребность в sparse/гибридном поиске или кластерности. Граница проходит примерно там, где ANN-запросы начинают конкурировать с транзакционной нагрузкой за ресурсы одной БД.

Нужна ли векторная БД, если я просто вызываю API большой LLM?

Да, если модель должна отвечать на основе ваших документов, которых нет в её обучающей выборке. Это и есть RAG: эмбеддинг запроса, поиск ближайших чанков, подстановка их в контекст. Без векторного хранилища ретривер не работает, а контекстные окна всё равно ограничены.

Что хранить в метаданных?

Минимум — исходный текст чанка (или ссылку на него), ID документа и поля, по которым будете фильтровать: тенант, язык, дату, категорию. Большие бинарные данные и полные документы держите в основном хранилище, в векторной БД оставляйте ссылки — так индексы легче, а снапшоты быстрее.

Не уверены, какое хранилище выживет под вашей нагрузкой и требованиями к данным? Напишите мне — разберём архитектуру конкретно под ваш проект.

Обсудить проект

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

Написать мне
Вернуться к блогу