Vector DB: pgvector, Qdrant и ANN-индексы на практике

Соберёте векторный поиск на pgvector и Qdrant и сможете цифрой показать, сколько нужных документов теряет индекс, сжатие или фильтр по сравнению с точным поиском. Векторный поиск ломается тихо. Фильтр по доступу поверх HNSW порой отдаёт 0 строк из 10, а замер на маленькой таблице показывает recall 1,0, хотя планировщик обошёл индекс. Ставить ничего не нужно: решения запускаются в песочнице с PostgreSQL, pgvector 0.8 и готовыми векторами модели e5-small. Ключи API не нужны.

Чему вы научитесь

Чем меньше читает индекс, тем больше теряет

Индекс быстрее перебора, потому что не читает часть базы. Сколько нужных документов он при этом теряет, без замера не скажет никто. В курсе цену экономии меряют двумя числами на одном наборе: recall@10 против точного перебора и сколько векторов реально прочитано.

В лабе вы пишете IVF на numpy: k-means без sklearn и faiss, инвертированные списки, проход по nprobe ближайшим кластерам. В нашем прогоне на 1000 векторах при nprobe 8 recall@10 вышел 0,955, прочитано 265 векторов. При nprobe 32 читается вся база плюс центроиды, и это дороже перебора.

Сначала EXPLAIN: на таблице в тысячу строк PostgreSQL обходит индекс полным перебором, и замер честно показывает recall 1,0, хотя индекс ни при чём. В pgvector рычаг тот же: число кластеров (lists) задают при построении, а сколько из них читать (probes), задают в запросе. При lists 30 для recall 0,96 хватило probes 4, а проверка следит, чтобы с меньшим значением планка не бралась.

Что нужно знать

Решения пишутся на Python, в одном файле solution.py. Если ваш основной язык Go, хватит базового Python: циклы, списки, словари.

numpy: матричное умножение @, argsort, срезы. Про argpartition расскажем в курсе.

SQL на уровне SELECT, WHERE, ORDER BY и CREATE INDEX. Подключение из Python через psycopg 3 показано в теории.

Эмбеддинги и метрики близости знать заранее не нужно: курс начинается с них.

Сжали в четыре раза, вернули соседей пересчётом

Скалярная квантизация int8 уменьшает вектор с 1536 до 384 байт. Но часть нужных документов теряется: на тех же 1000 векторах и 50 запросах recall@10 падает до 0,812.

Пересчёт возвращает их. По int8 берут 30 кандидатов, считают для них расстояние по float32 и оставляют 10 лучших. Recall@10 поднимается до 0,996.

Ловушка лабы: произведение int8 на int8 переполняет тип, и порядок кандидатов превращается в шум. Считать нужно в int32, иначе решение не примут. Локальный Qdrant принимает quantization_config, но не квантует, поэтому механику вы воспроизводите в numpy.

Recall 0,99 и 100 байт на вектор: такого варианта нет

Точный перебор, IVF и PQ сравниваются на одном наборе. У каждого четыре числа: recall, время, сколько векторов прочитано и сколько байт уходит на вектор.

Выбор делается в два шага: сначала отсекают варианты по жёстким ограничениям, потом берут лучший по одному критерию. Если нужен recall не ниже 0,90 при минимуме чтений, выбирают IVF: перебор тоже проходит, но читает всю базу. При лимите в 100 байт остаётся только PQ с 48 байтами и recall 0,608.

Требование «recall 0,99 и не больше 100 байт» не выполняет никто. Правильный ответ в лабе null. Наименее плохой вариант молча нарушил бы требование. Дальше меняют nprobe и M или пересматривают требования. Так же выбирают индекс на работе: сначала требования, потом замер, и честное «не получится», если требования несовместимы.

Поместятся ли 5 млн векторов в 2 GiB

pgvector берут, когда записи уже лежат в PostgreSQL и нужны транзакции и JOIN. Qdrant подходит для больших коллекций, квантизации и векторов на диске. Одни и те же задачи курс разбирает для обеих баз: метрика, индекс, фильтр, сжатие, смена модели без простоя. Картинка выше показывает, как каждая называется в pgvector и в Qdrant.

Последняя лаба про расчёт памяти: сырые векторы плюс связи графа. По формулам курса 5 млн векторов размерности 384 в float32 занимают 7,75 GiB и в бюджет 2 GiB не помещаются. С PQ-кодами по 48 байт выходит 0,82 GiB, но recall на нашем наборе падает до 0,608. Цена экономии видна сразу. В pgvector своего PQ нет, там экономят через halfvec и bit, а PQ есть в Qdrant.

Формулу проверяют на живой таблице: модель размера таблицы на 1100 векторах должна разойтись с pg_relation_size не больше чем на 2%. Индекс HNSW в pgvector хранит копию векторов, поэтому на диске он занимает примерно вдвое больше, чем сами векторы.

Для кого

Бэкенд-разработчикам, которым к PostgreSQL добавили поиск по смыслу и нужно понять, почему выдача с фильтром по доступу короче LIMIT.

Инженерам данных, которые загружают и переиндексируют эмбеддинги: без дублей при повторном запуске и без простоя при смене модели.

Тем, кто выбирает между pgvector и Qdrant и должен обосновать выбор цифрами памяти и recall.

Поддержка

Вопросы по курсу можно задать поддержке в Telegram: t.me/mentorix_support.

Программа курса

Модулей: 5, уроков: 21. Задачи с кодом проверяются автоматически.

  1. Эмбеддинги и сходство

  2. pgvector: векторы в PostgreSQL

  3. Qdrant: специализированная векторная БД

  4. ANN-алгоритмы изнутри

  5. Векторный поиск в продакшене

Стоимость курса: 7990 ₽

Открыть курс на Mentorix · Все курсы