От поиска по архивам документов и медиафайлам до рекомендательных систем и AI приложений — всюду работают эмбеддинги и векторный поиск. Но когда дело доходит до выбора конкретного инструмента, глаза разбегаются: Qdrant, Milvus, Weaviate, Redis, Elasticsearch, Pgvector…

Если вы:

  • планируете внедрять семантический поиск в свой продукт,

  • выбираете между проверенными временем БД и специализированными системами обработки векторов,

  • ищете независимые бенчмарки,

то этот материал — для вас. Мы разберем основные концепции векторного поиска, сравним популярные open-source решения и протестируем скорость их работы с учетом загрузки процессора и памяти.

Основные концепции векторного поиска

Эмбеддинги

Одна из ключевых проблем информационно-поисковых систем — наличие семантического разрыва. Это разница между тем, как люди интерпретируют и понимают смысл информации, и тем, как она представлена и обрабатывается алгоритмами.

Представление объектов в виде последовательностей слов текста, пикселей изображений, числовых значений звуковых сигналов и т. п. недостаточно для эффективного решения задач поиска. Эти признаки низкоуровневые и отражают лишь поверхностные свойства объекта. Для качественного поиска необходим переход к выявлению смыслов и связей внутри обрабатываемых данных. Развитие моделей машинного обучения и нейронных сетей позволило приблизиться к решению этой задачи. Удалось построить модели, преобразующие низкоуровневые признаки в семантические вектора (эмбеддинги). Пространство таких векторов обладает внутренней структурой, при которой семантически близкие объекты расположены рядом, а различающиеся — максимально далеко.

Одномодальные и мультимодальные модели

Одномодальные модели (BERT, SBERT, RoBERTa, USE, BGE, ResNet, ViT, AST и т.д.) преобразуют практически любой объект — будь то текст, изображения или аудио — в компактные вектора семантических признаков, позволяющие сравнивать объекты внутри одной модальности.

Мультимодальные модели (CLIP, MVAE, LXMERT и т.д.) направлены на объединение и представление информации из разных модальностей в общем векторном пространстве. Такие модели открывают возможности для кросс-модального поиска, например, можно находить изображения по текстовым запросам или автоматически создавать текстовые описания к изображениям.

Одномодальные и мультимодальные модели
Одномодальные и мультимодальные модели

Плотные и разреженные вектора

Эмбединги можно классифицировать по сруктуре их векторов. Если большинство значений вектора ненулевые, вектор считается плотным (dense), иначе — разреженным (sparse).

Компоненты разрежённых векторов обычно имеют явный семантический смысл. Например, текст можно описать разрежённым вектором, где каждой компоненте соответствует отдельное слово или фрагмент (токен), а её значение отражает важность этого слова (вес) для конкретного документа. Такие веса могут быть рассчитаны на основе статистических алгоритмов TF-IDF (Term Frequency-Inverse Document Frequency), BM25 (Best Match 25, улучшенная вариация TF-IDF с учётом длины документа) или на основе нейросетевых моделей, например, SPLADE (Sparse LAtent Document Encoder). В отличие от статистических алгоритмов, нейросетевые модели могут обогащать результирующий вектор весами семантически близких токенов для каждого входящего слова.

Модель разреженных векторов
Модель разреженных векторов

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

Для экономии памяти и ускорения поиска sparse-вектора хранят только индексы и значения ненулевых элементов. Для индексирования используются обратные индексы (inverted index) — структуры, связывающие термин со списком документов, в которых он содержится.

Плотные эмбеддинги по сравнению с разрежёнными имеют относительно небольшую размерность. Каждое измерение представляет некий абстрактный семантический признак. Сами по себе компоненты такого вектора сложны для прямой интерпретации, но они хорошо улавливают связи и контекстный смысл, что делает их подходящими для задач семантического поиска.

Плотные и разреженные вектора
Плотные и разреженные вектора

Одновекторные и мультивекторные модели

В одновекторных моделях объект описывается одним вектором с фиксированной или переменной размерностью. Эмбеддинги переменной размерности, такие как «эмбеддинги-матрёшки», кодируют информацию иерархически — несколько уровней детализации вкладываются в один вектор. Полный эмбеддинг хранит полную семантику, а каждый вложенный вектор (первая половина, первая четверть и т. д.) содержит частичное представление. Это отличает их от эмбеддингов фиксированной размерности, в которых обрезание размерности обычно разрушает семантический смысл. Модели с динамической размерность позволяют выбирать степень детализации и адаптироваться к разным задачам, сбалансировав точность и вычислительную нагрузку.

Модели плотных векторов
Модели плотных векторов

Если объект имеет сложную структуру и включает в себя несколько независимых друг от друга смысловых аспектов, одновекторного описания для него может быть недостаточно. Например, представление длинного разнородного текста одним вектором может размыть важную информацию в процессе усреднения и негативно сказаться на качестве поиска. Мультивекторные эмбеддинги, такие как ColBERT, ColPali или ColQwen, описывают один объект несколькими векторами, каждый из которых представляет отдельную часть (текстовый элемент, участок изображения). Такой подход помогает избежать потери ценной информации и обеспечивает более точное сравнение сложных объектов через сопоставление их составляющих, повышая общую релевантность поиска.

Методы оценки релевантности

Релевантность — ключевой критерий качества поиска, который оценивает, насколько хорошо документ соответствует заданному поисковому запросу. В векторном поиске можно выделить три подхода, используемые при оценке релевантности:

  • модели без взаимодействия (No-interaction Retrieval Models),

  • модели полного или раннего взаимодействия (Full Interaction Retrieval Models),

  • модели с поздним взаимодействием (Late Interaction Retrieval Models).

Здесь «взаимодействие» означает процесс сопоставления частей запроса и документа.

Модели без взаимодействия

В модели без взаимодействия запрос и документы коллекции обрабатываются отдельно и независимо друг от друга. Для каждого документа заранее вычисляется один вектор. Во время поиска вычисляется вектор запроса, который потом сравнивается с векторами документов. Запрос и документы не взаимодействуют напрямую во время оценки релевантности — сравниваются только их векторные представления. Такой подход обеспечивает высокую скорость и масштабируемость поиска, но снижает точность из-за потери связей между отдельными частями запроса и документа.

Модель поиска без взаимодействия
Модель поиска без взаимодействия

Оценкой релевантности в этом случае выступают функции сходства (чем больше значение, тем больше похожи вектора) или расстояния (чем меньше значение, тем больше похожи вектора). Распространенными примерами таких функций являются евклидово расстояние, косинусное сходство, скалярное произведение. Они по своему чувствительны к отдельным аспектам: евклидово расстояние учитывает дистанцию между точками; косинусное сходство оценивает ориентацию векторов, игнорируя их длину; скалярное произведение учитывает и величину, и направление сравниваемых векторов. Правильный выбор функции сравнения влияет на точность поиска. Эксперименты с подбором метрики лучше начинать с той, которая применялась при обучении используемой модели эмбеддингов — в большинстве случаев это будет оптимальным вариантом.

Если объекты представлены бинарными векторами, элементы которых отражают либо наличие, либо отсутствие определенных тегов или меток, сравнение может выполняться с использованием функции оценки подобия строк и множеств, таких как расстояние Хемминга или расстояние Жаккарда.

Модели полных взаимодействий

Модели полных взаимодействий обрабатывают поисковый запрос и документы совместно. Запрос конкатенируется с каждым документом, после чего они вместе проходят через кросс-энкодер с ранжирующим слоем, который выдает оценку релевантности. Поскольку модель одновременно видит содержимое запроса и документа, метод позволяет выявлять тонкие контекстуальные связи между ними. Но из-за того, что все вычисления выполняются в момент запроса, метод плохо масштабируется для больших объёмов данных.

Модель поиска с полным взаимодействием
Модель поиска с полным взаимодействием

Модели позднего взаимодействия

В модели позднего взаимодействия запрос и документ отдельно кодируются в многовекторные эмбеддинги и на их основе сравниваются по частям в процессе поиска. Релевантность оценивается с помощью функции MaxSim, где для каждого вектора запроса вычисляется максимальное сходство с векторами документа, затем эти максимумы суммируются в итоговую оценку.

Модель поиска с поздним взаимодействием
Модель поиска с поздним взаимодействием

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

Сценарии поиска

Одновекторный поиск

Традиционным методом векторного поиска является поиск по одному плотному вектору с оценкой релевантности по модели без взаимодействия.

Одновекторный поиск
Одновекторный поиск

Преимущества: высокая скорость поиска и относительно небольшие затраты на хранения векторов и индексов.

Недостатки: ограниченная способность учитывать многогранность или неоднозначность документов и запросов.

Многовекторный поиск

Многовекторный поиск предполагает представление одного объекта или документа с помощью нескольких эмбеддингов, каждый из которых отражает отдельный аспект или модальность объекта (например, текстовое описание, изображения, аудио). Результат поиска является объединением результатов одновекторных поисков, выполненных по каждому векторному полю с последующим переранжированием. Метод может использоваться, например, для поиска по длинным документам, разбитым на фрагменты или поиска объектов по изображениям и текстовым описаниям одновременно.

Многовекторный поиск
Многовекторный поиск

Преимущества: возможность более точного и всестороннего поиска.

Недостатки: по сравнению с одновекторным подходом скорость поиска ниже, а затраты на хранение векторов больше.

Гибридный поиск

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

Гибридный поиск
Гибридный поиск

Преимущества: повышается релевантность поиска благодаря объединению сильных сторон векторного и лексического поиска.

Недостатки: снижается скорость поиска из-за необходимости выполнять несколько поисковых операций и последующего объединения полученных результатов.

Cлияние и переранжирование

Слияние и переранжирование — это этапы пайпланов обработки результатов поиска для повышения его релевантности.

Силяние (fuse) используется для объединения результатов из нескольких источников поиска. Порядок результатов в объединенном списке определяется функциями ранжирования на основе ранее полученных оценок релевантности. Примерами таких функций являются Weighted Ranker и Reciprocal Rank Fusion (RRF).

Weighted Ranker приводит ранее полученные оценки к единому диапазону значений с помощью нормализации и вычисляет итоговую оценку как взвешенную сумму.

Weighted Ranker
Weighted Ranker

Reciprocal Rank Fusion получает объединенную оценку на основе позиций результатов (рангов) в разных списках.

Reciprocal Rank Fusion
Reciprocal Rank Fusion

Семантическое переранжирование (rerank) может использоваться как для уточнения отдельно взятого поиска, так и для объединения результатов из разных источников. В отличие от fuse методов, существующие оценки игнорируются, и релевантность каждого результата вычисляется заново для пары «запрос-документ». Для пересчёта может быть использован любой более точный метод оценки, например, по модели позднего взаимодействия или через кросс-энкодер.

Fuse и Rerank могут дополнять друг друга в сложных пайплайнах векторного поиска, например, сначала результаты объединяются из нескольких источников поиска на основе их первичных оценок, а затем выполняется уточнение на базе семантического переранжирования. Эффективность той или иной стратегии определяется экспериментально и зависит от конкретной задачи.

Индексы

Базовая операция в векторной базе данных — это поиск k-ближайших соседей (k-nearest neighbors, KNN), который находит k векторов, наиболее похожих на заданный вектор запроса.

Можно точно найти k-ближайших соседей полным перебором (brute force), но это вычислительно крайне затратно. Индексирующие структуры, такие как k-мерные деревья (k-d tree) и деревья шаров (Ball trees), становятся неэффективными из-за проклятия размерности. По мере увеличения размерности объём пространства растёт экспоненциально, из-за чего данные становятся всё более разреженными и большинство узлов в таких индексах будет содержать очень мало или очень много точек, что снижает скорость поиска.

Из-за этих эффектов системы векторного поиска используют алгоритмы приближенного поиска ближайших соседей (Approximate Nearest Neighbors, ANN), жертвуя точностью ради значительного увеличения скорости. Их можно классифицировать по методам поиска и способам сжатия индексируемых векторов.

Индексы на деревьях

В этихиндексах ускорение выполняется за счет построенных бинарных деревьев, в которых похожие векторы попадают в одни поддеревья. Annoy (Approximate Nearest Neighbours Oh Yeah) является примером такого индекса. В нем бинарное дерево строится путем рекурсивного разбинения пространства гиперплоскостью . Гиперплоскость выбирается путем случайного выбора двух точек из подмножества и проведением гиперплоскости, равноудалённой от них. Разбиение продолжается до тех пор, пока в одном из узлов дерева не окажется заданное количество векторов. Такие индексы хорошо работают с низкоразмерными данными, но при высоких размерностях уступают по скорости и точности другим альтернативам.

Графовые индексы

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

  • HNSW — Hierarchical Navigable Small World, иерархический граф, хранящийся в RAM.

  • DiskANN — плоский граф. В RAM хранится компактный навигационный уровень по квантованным векторам, на SSD-диске хранится основной граф, нарезанный на сегменты.

Главные преимущества — высокая скорость и точность на больших датасетах. Недостатки: низкая скорость индексации.

Кластерные индексы

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

Примеры таких индексов:

  • IVF (Inverted File Index).

  • SCANN — индекс с анизотропным векторным квантованием.

  • SPANN — дисковый индекс, в котором центроиды кластеров хранятся в RAM, а их вектора на SSD диске.

Точность поиска у них, как правило, ниже, чем у графовых индексов, однако они эффективнее по расходу памяти.

Квантование векторов

Квантование — это процесс преобразования значений некоторой величины (обычно непрерывной) в конечное число дискретных значений (codebook). Функция квантования сопоставляет каждому входному значению (точке) ближайшее квантованное значение из кодового словаря.

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

Скалярное квантование

Скалярное квантование (SQ) — один из простых способов поэлементного сжатия векторов. Диапазон значений компонент разбивается на конечное число интервалов, и значения заменяются номерами интервалов, в которые они попали. Например, при разбиении на 256 интервалов 32-битные числа с плавающей точкой можно заменить целыми 8 битными числами, сокращая размер вектора в 4 раза.

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

Скалярное квантование
Скалярное квантование

Бинарное квантование

Бинарное квантование (BQ) преобразует каждый вектор эмбеддинга в бинарное представление.

В простом варианте такое квантование можно рассматривать как частный случай скалярного с разбиением диапазона значений на два интервала: числа больше нуля кодируются единицей, остальные — нулем. Такая реализация может может неплохо работать на векторах с числом измерений больше 1024, но при меньших размерах вектор становится недостаточно информативен и заметно снижается точность поиска.

Бинарное квантование
Бинарное квантование

Улучшенный вариант бинарного квантования реализуется с помощью алгоритма RaBitQ. В этом случае квантуются не компоненты исходного вектора, а нормализованная разность между исходным вектором и центроидом кластера, к которому он принадлежит. При квантовании дополнительно сохраняется расстояние до центроида и угол между вектором разности и его квантованной версией. Эти дополнительные данные позволяют точнее вычислять расстояние между векторами по их квантованным представлениям, частично компенсируя утраченную при квантовании информацию. С целью более равномерного распределения информации по всем измерениям и уменьшения ошибки квантования, к векторам может применяться операция случайного поворота.

Product Quantization

В Product Quantization (PQ) векторы исходного набора данных разбиваются на несколько равных частей или подвекторов. Для каждого подвектора формируется собственный codebook — набор центроидов, полученный на соответствующем подмножестве подвекторов с использованием алгоритма кластеризации, например, K-means. В ходе квантования каждый подвектор исходного вектора заменяется индексом ближайшего центроида из соответствующего codebook. В результате  уменьшается не только размер компонентов вектора, но и их количество.

Product Quantization
Product Quantization

ADC и переоценка релевантности

Чем больше данных, тем сильнее хочется их сжать. Однако сжатие с потерями неизбежно ведет к феномену коллапса информативности — разные вектора исходного пространства получают идентичные квантованные представления и становятся неотличимыми друг от друга. По мере роста количества векторов увеличивается вероятность таких коллизий, что закономерно приводит к снижению точности поиска.

Один из способов повысить точность заключается в использовании ассиметричного квантования. В контексте векторного поиска — это подход, при котором вектор запроса и векторы базы данных используют разную степень сжатия (с разной детализацией). Например, векторы базы хранятся в квантованном виде с низкой битностью, в то время как вектор запроса подвергается менее агрессивному квантованию либо вовсе остается в своем первоначальном виде. Во время поиска между векторами вычисляется ассиметричное расстояние (Asymmetric Distance Computation, ADC), которое является более точной оценкой, в отличие от симметричного подхода, где и запрос, и векторы базы данных квантованы одинаково.

Второй способ улучшить качество поиска на сжатых данных — использовать переоценку релевантности по оригинальным векторам. Сначала выполняется поиск по квантованным векторам с избыточной выборкой кандидатов (oversampling), а затем делается их переоценка на основе точного расчёта расстояний по оригинальным векторам.

Переоценка релевантности
Переоценка релевантности

Методы фильтрации

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

Сочетание семантического поиска с фильтрацией — задача непростая. Сложность заключается в том, что во многих случаях метаданные объектов слабо зависят от их семантического значения. Т.е. вектора, которые удовлетворяют условию фильтрации, называющиеся целевыми векторами, могут располагаться непредсказуемо далеко относительно вектора запроса. Характер их распределения оценивается с помощью корреляции (Query Correlation). Когда целевые вектора сгруппированы и формируют кластер, то корреляция положительна, если вектор запроса находится в окрестности этого кластера, и отрицательна, если располагается за его пределами. Если целевые вектора не образуют компактного кластера, корреляция близка к нулю.

Query Correlation
Query Correlation

Рассмотрим основные методы фильтрации по метаданным.

Отдельные подындексы

Вместо одного общего базового индекса для всех векторов строятся дополнительные подындексы для разных значений метаданных:

  • когда фильтров нет, используется базовый индекс;

  • когда условие фильтрации есть, происходит переключение на подходящий подындекс.

Например, при поиске с фильтром вида «цвет=синий» используется отдельный индекс, соответствующий этому предикату. Этот подход максимально точен при различной корреляции с вектором запроса и селективностью фильтра, но несет дополнительные накладные расходы по памяти и скорости индексации и не является очень практичным в случае большого или неограниченного множества предикатов.

Постфильтрация

Фильтрация реализована отдельным этапом: сначала выполняется векторный поиск, который предварительно отбирает потенциальных кандидатов, а затем из результатов удаляются те, что не соответствуют фильтру. В случаях, когда нет положительной корреляции с запросом или фильтр отбрасывает большое кол-во объектов, постфильтрация становится непредсказуемой с точки зрения точности. Появляется неопределенность в количестве кандидатов, которое требуется искать на первом этапе. При недостаточной выборке после векторного поиска существует риск, что исходный результат не будет содержать ни одного соответствия. В определенной степени эту проблему можно решать с помощью итеративной фильтрации (Iterative filtering), когда в цикле повторяется векторный поиск и постфильтрация до тех пор, пока не получим требуемое кол-во результатов или не выполнится дополнительное условие остановки. На каждой итерации мы просматриваем новые порции кандидатов. Тем самым улучшается точность, но при этом увеличивается время поиска.

Постфильтрация
Постфильтрация

Предфильтрация

Фильтрация реализована отдельным этапом: сначала выполняется поиск по метаданным, а затем среди отобранных объектов проводится семантический поиск. Если количество записей после фильтра небольшое, последующий векторный поиск можно сделать полным перебором (brute-force). В остальных случаях появляется потребность в индексе. Можно использовать графовый индекс с передачей списка разрешенных кандидатов в качестве контекста поиска. Этот список формируется на этапе фильтрации и содержит идентификаторы векторов, которые удовлетворяют критериям фильтра. При обходе графа рассматриваются только те узлы, идентификаторы, которые есть в этом списке. Однако, если фильтр будет строгий или будет низкая корреляция с запросом, полное игнорирование отфильтрованных точек приведет к снижению точности поиска из-за разрыва связей в графе и образования «островов» с недоступными узлами. Для решения проблемы нарушения связности можно просматривать часть отфильтрованных узлов: даже если узел не соответствует фильтру, мы всё равно можем посетить его во время поиска с некоторой вероятностью. Эта вероятность зависит от коэффициента фильтрации — чем строже фильтр, тем больше нужно посещать таких узлов.

Предфильтрация
Предфильтрация

Фильтруемый векторный индекс

Используется один общий (predicate-agnostic) графовый индекс, адаптированный для фильтрации. Поиск выполняется по подграфу, узлы которого удовлетворяют заданному критерию фильтрации (predicate-subgraph). В отличие от обычных графовых индексов, в фильтруемых строятся дополнительные ребра для борьбы с потерей связности при фильтрации узлов, повышая вероятность нахождения валидных путей даже при высокой селективности фильтра. Примерами таких решений являются индексы на основе ACORN (Approximate Nearest Neighbor Constraint-Optimized Retrieval Network) алгоритмов.

Гибридный подход

Эффективность и точность отдельных методов зависит от размера данных, селективности фильтра и корреляции с вектором запроса, а также требуемой выразительности семантики запросов. Вместо поиска универсального решения, можно использовать гибридный подход: при анализе метаданных можно оценивать кардинальность или селективность фильтра и выбирать подходящую стратегию поиска. Например, когда кол-во потенциальных результатов после постфильтрации невелико, можно использовать полный перебор, в ином случае переключаться на итеративную фильтрации или фильтруемый векторный индекс.

Из чего выбирать

Векторный поиск можно реализовать на основе отдельных библиотек вроде Facebook Faiss, HNSWLib, Google ScaN. Это неплохой вариант для прототипов и исследовательских проектов, но у таких решений есть ряд функциональных ограничений. Например, нет фильтров по метаданным, отсутствуют механизмы отказоустойчивости, масштабируемости, аутентификации и т. п. При необходимости всё это придётся реализовывать и поддерживать самостоятельно на уровне приложения. Альтернативный вариант — использование готовых баз данных и специализированных систем векторного поиска, которые обеспечивают расширенный функционал и комплексный подход к управлению данными.

Рассмотрим популярные self-hosted решения с доступным исходным кодом: pgvector, pgvecto-rs, milvus, weaviate, redis, qdrant, vespa, elasticsearch, chroma.

Ниже представлены сравнительные таблицы по разным аспектам векторного поиска. Все рассматриваемые кандидаты поддерживают:

  • Векторный поиск с фильтрацией по метаданным;

  • Полнотекстовый поиск;

  • HNSW-индекс;

  • Основные функции сравнения векторов — скалярное произведение, косинусное подобие или угловая дистанция и Евклидово расстояние (метрики Dot product и Inner Product в контексте векторного поиска являются взаимозаменяемыми и вычисляются одинаково как сумма произведений соответствующих компонент вектора).

Используемые методы фильтрации:

  • Weaviate, Vespa, Elasticsearch. Гибридный подход с динамическим переключением между Filterable Vector Index и предфильтрацией с брутфорс поиском.

  • Qdrant. Гибридный подход с динамическим переключением между Filterable Vector Index и Payload index.

  • Redis. Гибридный подход с динамическим переключением между итеративной фильтрацией и предфильтрацией с брутфорс поиском.

  • Milvus, Pgvector, Pgvecto-rs. Итеративная фильтрация.

  • Chroma. Предфильтрация c HNSW индексом.

Weaviate и Chroma могут рассматриваться как альтернативы библиотекам благодаря возможности их запуска непосредственно в клиентских приложениях. Мы получаем расширенный функционал векторного поиска без накладных расходов, связанных с разворачиванием отдельной базы данных. Это особенно ценно, когда постоянное сетевое подключение отсутствует или ограничено, например, в приложениях для интернета вещей (IoT), мобильных устройствах или автономных системах.

Если в компании уже используются PostgreSQL, Redis и Elasticsearch, это хорошая точка входа, позволяющая реализовать основной функционал векторного поиска, не перестраивая архитектуру. При этом специализированные системы предлагают более богатый инструментарий, гибкость и удобство, особенно при разработке сложных пайплайнов поиска.

В качестве примера рассмотрим обобщенный рейтинг от менее компромисных по функционалу решений к более компромисным:

  1. Milvus. Есть поддержка разреженных векторов, гибридного поиска, индексов с квантованием, дисковых и GPU индексов. Работа с мультивекторными эмбеддингами в Milvus реализована через хранение каждого вектора отдельной записью с общим doc_id и вычислением функции MaxSim на стороне клиента при поиске, что не так удобно, как это сделано, например, в Qdrant или Weaviate.

  2. Qdrant. Есть поддержка разреженных векторов, гибридного поиска, мультивекторных эмбеддингов, индексов с квантованием и построения индекса на GPU. Отсутствует поддержка дисковых индексов.

  3. Vespa. Есть поддержка разреженных векторов, гибридного поиска, индексов с компрессией, дисковых индексов, мультивекторных эмбеддингов. Отсутствует поддержка GPU индексов.

  4. Elasticsearch, Pgvector, Pgvecto-rs. Есть поддержка разреженных векторов, гибридного поиска, индексов с квантованием, мультивекторных эмбеддингов. Отсутсвуют поддержка дисковых и GPU индексов. В Pgvector максимально допустимая размерность плотных векторов ограничена 2000, что может быть недостаточно для некоторых моделей эмбеддингов в области обработки естественного языка или компьютерного зрения; если хранить вектора в 16-битном halfvec типе, размерность можно увеличить до 4000.

  5. Weaviate. Есть поддержка гибридного поиска, мультивекторных эмбеддингов, индексов с квантованием и построение индекса на GPU. Отсутствует поддержка разреженных векторов и дисковых индексов.

  6. Redis. Есть гибридный поиск и индекс с квантованием. Отсутствует поддержка разреженных векторов, дисковых и GPU индексов, мультивекторных эмбеддингов.

  7. Chroma. Отсутствует поддержка разреженных векторов, гибридного поиска, индексов с квантованием, дисковых и GPU индексов, мультивекторных эмбеддингов.

Значимость каждой функции определяется конкретными сценариями использования, поэтому этот рейтинг не универсальный. При его составлении в первую очередь я учитывал поддержку гибридного поиска, мультивекторных эмбеддингов, разреженных векторов, индексов с квантованием. Во вторую — возможность построения индекса на GPU. И в третью — дисковые индексы, оптимизированные для работы с диском и хранящие часть данных в памяти, а часть на диске (альтернатива горизонтальному масштабированию для больших индексов, не помещающихся в RAM).

Немаловажным фактором при выборе БД является наличие хорошей документации. В этом плане можно отметить Qdrant, Milvus и Weaviate: помимо самой документации, их блоги содержат множество статей с детальным разбором механизмов векторного поиска.

Что на практике

Можно найти много готовых результатов тестирования векторных баз данных, в том числе от самих производителей, например:

По отдельности они не охватывают весь рассматриваемый нами список решений. А из-за отличий в версиях, конфигурациях БД и железе, готовые результаты разных бенчмарков трудно сравнимы между собой и в отдельных случаях являются противоречивыми (в тестах от производителей в целом побеждает тот, кто этот тест проводит). Поэтому логичней проводить собственное сравнительное тестирование. Для таких задач есть ряд open-source решений:

Мы выбрали фреймворк от qdrant, добавив в него поддержку PGVectors, Chroma и Vespa, а также возможность собирать метрики использования CPU и памяти через Prometheus.

Тестируемые версии

  • chromadb/chroma:1.0.3

  • bitnami/elasticsearch:8.18.0

  • milvusdb/milvus:v2.4.1

  • pgvector/pgvector:0.8.0-pg17

  • tensorchord/pgvecto-rs:pg16-v0.4.0

  • qdrant/qdrant:v1.14.1

  • redis:8.0

  • vespaengine/vespa:8.543.14

  • cr.weaviate.io/semitechnologies/weaviate:1.31.4

Сценарий тестирования

  1. На сервере в докер контейнере запускается пустой экземпляр тестируемой БД.

  2. На клиентской машине запускается клиент, создающий коллекцию с индексом.

  3. В созданную коллекцию загружается датасет векторов.

  4. После загрузки данных, если индекс строится асинхронно, клиент ожидает его готовности.

  5. Когда датасет проиндексирован, запускается поиск. Вектора для поиска берутся из тестовой выборки, для которой известны правильные ответы.

Измеряемые параметры

  • Upload time — время загрузки датасета.

  • Post upload time — время ожидания готовности индекса.

  • Recall — доля истинных ближайших соседей среди возвращённых результатов поиска.

  • Mean response time — среднее время ответа на поисковый запрос.

  • P95 response time — время ответа, в которое укладывается 95% поисковых запросов.

  • Requests-per-Second (RPS) — количество поисковых запросов в секунду.

  • Avg CPU и Avg RAM — средняя загрузка процессора и оперативной памяти на всех этапах тестирования.

Условия проведения экспериментов

  • Все тесты запускались на одних и тех же машинах, чтобы исключить влияние различий в аппаратном обеспечении.

  • Машина с клиентом: Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz, RAM 32GB DDR4 2133 MHz.

  • Машина с БД: Intel(R) Xeon(R) Silver 4216 CPU @ 2.10GHz, RAM 250GB DDR4 3200 MHz.

  • Контейнеры БД запускаются без установки лимитов на ресурсы.

  • Загрузка и поиск векторов выполняется в 8 потоков. Распараллеливание запросов в python-клиенте делается через multiprocessing с помощью fork метода.

  • Датасеты загружаются батчами по 64 вектора.

  • Для поиска используется HNSW индекс. Количество кандидатов-соседей, рассматриваемых для установления связи в процессе построения индекса, равно 512. Количество рёбер, которое узел может иметь в графе — 32.

  • Точность поиска регулируется только изменением ef параметра, отвечающего за количество просматриваемых в процессе поиска соседних узлов.

  • Скорости поиска сравниваются для конфигураций, при которых Recall равен 0.99±1%.

Тестируемые датасеты

Датасет

Описание

Размерность вектора

Кол-во векторов

Кол-во запросов

Функция сравнения

DBPedia OpenAI

Текстовые эмбеддинги, сгенер��рованые с помощью модели OpenAI text-embedding-3-large на датасете DBpedia

1536

1 000 000

10 000

Косинусное сходство

Gist-960-euclidean

Векторы признаков изображений, сгенерированные с использованием алгоритма GIST (gastrointestinal stromal tumor) на датасете Texmex.

960

1 000 000

1 000

Евклидово расстояние

Deep-image-96-angular

Эмбеддинги изображений, полученные моделью GoogLeNet, обученной на датасете ImageNet.

96

9 990 000

10 000

Косинусное сходство

Результаты экспериментов

В качестве дополнительных сравнительных метрик ниже построены диаграммы скорости индексации и поиска относительно средней утилизации CPU.

На разных датасетах наблюдается разное поведение БД, поэтому при составлении обобщенного рейтинга без условностей не обойтись. Всё зависит от того, как и какие критерии ставить во главу угла. Если в первую очередь учитывать абсолютные показатели времени индексации и RPS, а во вторую — эффективность утилизации ресурсов, то на основе рассматриваемых датасетов можно выделить следующий топ-3 решений для одно-векторного поиска без фильтрации:

  1. Redis. В топах и по скорости индексации, и по скорости поиска на всех рассматриваемых датасетах, в том числе с учетом утилизации CPU. С точки зрения эффективности использования памяти Redis также находится на верхних позициях.

  2. Qdrant. Достаточно высокие абсолютные значения скорости поиска и индексации. По эффективности использования CPU заметно уступает Redis. Лидирует по эффективности использования памяти.

  3. Weavite, Milvus. Довольно компромиссные варианты. Оба решения в основном попадают в топ-4 по абсолютным показателям скорости индексации и поиска (исключением для Milvus является датасет DBPedia OpenAI). Milvus в топе по экономичности потребления памяти, однако обладает наименее эффективной реализацией поиска с точки зрения загрузки процессора. Weaviate потребляет значительно больше оперативной памяти по сравнению с Milvus, однако нагрузка на процессор при поиске существенно ниже при сопоставимых значениях RPS.

Заключение

Как и ожидалось, серебряной пули нет. Оптимальность выбора существенно зависит от конкретных сценариев использования, типа обрабатываемых данных и планируемого масштаба нагрузки. Тем не менее, надеюсь, что статья поможет сократить область поиска и подобрать подходящий инструмент для ваших проектов.

Комментарии (3)


  1. antonb73
    08.12.2025 10:18

    Хорошая статья. Спасибо.

    Weighted Ranker приводит ранее полученные оценки к единому диапазону значений с помощью нормализации и вычисляет итоговую оценку как взвешенную сумму.

    Мы использовали свой аналог, о котором я писал. WR слишком уж простой масштабатор.


    1. vikorbit Автор
      08.12.2025 10:18

      Спасибо за отзыв!


  1. S_A
    08.12.2025 10:18

    Спасибо, содержательно. А что скажете про typesense? не вошёл в список, я им частенько пользуюсь. нравится скорость и приличный api, гибридный поиск, простота развертывания, единственное, более 256 где-то ближайших не отдаёт, но обычно более 30-50 и не нужно.