
Итак, ваш проект вырос и вам потребовалась новая функциональность, будь то рекомендательный движок, база знаний или автоматизированная первая линия техподдержки. Для всего этого можно использовать векторный и/или семантический поиск, а также интегрировать в проект LLM. Поздравляю — теперь вам нужно еще и хранить embedding-векторы, а также искать по ним ближайшие объекты. Решений два: внешняя векторная БД или интеграция всего этого богатства в существующий стек. Второй путь проще на старте, немного быстрее и обычно дешевле — разумеется, если вы уже используете PostgreSQL.
Привет, Хабр! Меня зовут Александр Гришин, я отвечаю за развитие продуктов хранения данных в Selectel: облачных баз данных и S3-хранилища. В этой статье я расскажу о pgvector — расширении для PostgreSQL, которое позволяет добавить векторный поиск без внешних сервисов, пересборки архитектуры и большого количества работы. Материал пригодится продуктовым командам, архитекторам, бэкенд-разработчикам и инженерам данных.
Если вы это читаете, то, скорее всего, уже используете PostgreSQL. Возможно, вы не хотите сразу разворачивать отдельную инфраструктуру под векторный поиск, планируете попробовать работу с embedding'ами от OpenAI, Cohere, HuggingFace и других моделей, планируете строить или уже строите RAG-системы, рекомендательные движки, семантический поиск или базу знаний в MVP-формате.
Используйте навигацию, если не хотите читать текст целиком:
→ Зачем вообще нужен векторный поиск
→ Что такое pgvector
→ Развернем pgvector в облачной базе данных
→ Рассмотрим расширение детальнее
→ Что под капотом
→ Вывод
Зачем вообще нужен векторный поиск
Когда вы работаете с embedding'ами от LLM, вы неизбежно сталкиваетесь со следующей задачей. Приходит запрос клиента и нужно быстро находить похожие объекты по вектору.
Вот типовые примеры таких задач.
- Семантический поиск. В отличие от классического поиска по ключевым словам, семантический поиск позволяет находить документы по смыслу. Например, если пользователь ищет, как сделать бэкап БД, система найдет статьи, в которых не обязательно есть эти слова, но описаны релевантные процессы.
- RAG-пайплайны (Retrieval-Augmented Generation). Эта архитектура объединяет LLM и внешнюю базу знаний. Когда пользователь задает вопрос, система сначала находит релевантные документы по векторной близости, а затем передает их LLM для генерации ответа. Так работают продвинутые чат-боты, корпоративные ассистенты и интерфейсы к документации.
- Рекомендательные системы. Вместо жестких правил («похожие жанры», «покупали вместе») можно анализировать embedding-профили ваших пользователей и объектов. А после рекомендовать песни, статьи, фильмы, товары, даже если они текстово совершенно разные, однако имеют схожую векторную репрезентацию.
- Классификаторы. В embedding-пространстве можно обучать модели, которые определяют категорию, тональность или тематику документа. Один из подходов задать центроиды для каждого класса (например, «позитив», «негатив», «нейтрально»), а затем относить новый документ к ближайшему по векторному расстоянию. Также применяются k-NN-модели, которые ищут несколько ближайших примеров с известной меткой и определяют класс по большинству. Например, можно классифицировать отзывы пользователей по тону, статьи по теме, а письма по отделу, которому они адресованы.
Индустрия предлагает широкий спектр специализированных инфраструктурных решений для таких задач: Faiss, Milvus, Pinecone, Qdrant, Weaviate, Vespa. Но так как я люблю PostgreSQL, то не могу не рассказать о возможной альтернативе на ее базе. Это дает возможность пощупать новое без зоопарка технологий и разрозненных API — прямо в PostgreSQL.

Что такое pgvector
Итак, pgvector — это расширение к PostgreSQL, которое добавляет тип данных vector и операторы поиска по расстоянию. Поддерживаются три основные метрики сравнения векторов.
- L2 (евклидово расстояние). Классическое расстояние между двумя точками в многомерном пространстве. Хорошо работает, если векторы не нормализованы и распределение значений относительно равномерное.
- Cosine similarity (косинусное сходство). В отличие от предыдущей метрики измеряет не абсолютное расстояние, а угол между двумя векторами. Подходит для текстовых embedding'ов, где важна направленность, а не масштаб. Требует нормализации векторов (единичная длина).
- Inner product (внутреннее произведение). Скалярное произведение двух векторов. Может использоваться как прокси для оценки «сходства» при обучении моделей и в задачах ранжирования.
Выбор метрики влияет на операторный класс индекса и характер сортировки поблизости. Важно понимать, какая из них подходит под вашу задачу. Например, cosine similarity чаще используется в задачах семантического поиска, а inner product может быть полезен в рекомендательных системах.
Развернем pgvector в облачной базе данных
В облачном PostgreSQL от Selectel pgvector можно установить в пару кликов через панель управления. С полным списком поддерживаемых расширений можно ознакомиться в документации.
1. Разверните кластер в панели управления. Как это сделать, подробно описано в документации.

2. Создайте пользователя.

3. Создайте базу данных.

4. Добавьте расширение vector.

5. Подключитесь и используйте готовую облачную базу данных.

Рассмотрим расширение детальнее
Когда вы передаете текст в модель вроде text-embedding-3-small от OpenAI, она возвращает вектор с размерностью 1 536. Где каждый из 1 536 элементов — это числовое представление одного аспекта смысла или структуры текста. Такие векторы называются embedding'ами:
[0.134, -0.823, 0.521, ..., 0.004]
В pgvector это отражается в типе данных — embedding VECTOR(1536). И тогда PostgreSQL будет следить, чтобы каждый вектор точно содержал эти 1 536 значений. Для упрощения повествования дальше будем использовать только три значения.
Допустим, мы получаем на наш запрос именно такой вектор от модели OpenAI. Для хранения создаем таблицу с типом VECTOR:
CREATE TABLE items (
id SERIAL PRIMARY KEY,
title TEXT,
embedding VECTOR(1536)
);
Добавим данные для нескольких embedding-векторов разных объектов:
INSERT INTO items (title, embedding) VALUES
('PostgreSQL embeddings', '[0.10, -0.80, 0.45]'),
('Neural image processing', '[0.42, 0.18, -0.35]'),
('Sound pattern matching', '[-0.20, 0.70, 0.60]'),
('Document clustering', '[0.09, -0.79, 0.48]');
Теперь сравним их попарно и отсортируем по расстоянию:
SELECT
a.title AS title_a,
b.title AS title_b,
a.embedding <-> b.embedding AS distance
FROM items a
JOIN items b ON a.id < b.id
ORDER BY distance;
Здесь оператор
<->
вычисляет расстояние между двумя векторами.
Из вывода видно, что:
- «PostgreSQL embeddings» ближе всего к «Document clustering» — у них почти одинаковые векторы;
- «Sound pattern matching» и «Neural image processing» — ближе друг к другу, чем к текстовым статьям.
Таким образом, мы можем оценивать степень семантической близости любых объектов, представленных векторами. Какая именно метрика используется, зависит от операторного класса, заданного при создании индекса.
Как я уже говорил, в pgvector доступны три варианта. Рассмотрим каждый из них подробнее.
Евклидово расстояние: vector_l2_ops
Стандартная метрика, измеряющая «прямое» расстояние между точками в пространстве. Хорошо подходит, если векторы не нормализованы.
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops);
SELECT id, embedding, embedding <-> '[0.1, -0.8, 0.5]' AS distance
FROM items
ORDER BY embedding <-> '[0.1, -0.8, 0.5]'
LIMIT 3;
Разберем подробнее, что тут происходит.
- Создаем индекс. Тип индекса —
ivfflat
: это приближенный метод поиска ближайших соседей (Approximate Nearest Neighbor), реализующий IVF (inverted file index). - Указываем, что используется операторный класс
vector_l2_ops
— это значит, что сравнение векторов будет происходить по евклидову расстоянию (L2-норма). Индекс ускоряет поиск ближайших векторов по расстоянию. - Выбираем
id
, сам векторembedding
и вычисляем расстояние между этим вектором и заданным вектором-запросом '[0.1, -0.8, 0.5]'. - Оператор
<->
— это универсальный оператор «расстояния» в pgvector. В зависимости от выбранного индексного операторного класса (vector_l2_ops), он означает евклидово расстояние. - Сортируем по возрастанию расстояния и выводим три самых похожих вектора. Т. е. в первую очередь возвращаются векторы, находящиеся ближе всего к заданному.
Вывод может быть каким то таким:

Здесь
distance
— это скалярное значение, показывающее, насколько далеко embedding из таблицы находится от заданного нами вектора [0.1, -0.8, 0.5] в евклидовом пространстве. Чем меньше значение, тем ближе векторы.Косинусная мера: vector_cosine_ops
Этот операторный класс измеряет угол между векторами. Тут получается, что важно не расстояние, а направление. Подходит для embedding'ов текста, но требует нормализации векторов (длина = 1).
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops);
-- Все векторы должны быть нормализованы заранее
SELECT id, embedding, embedding <-> '[0.1, -0.8, 0.5]' AS similarity
FROM items
ORDER BY embedding <-> '[0.1, -0.8, 0.5]'
LIMIT 3;
Разберем подробнее работу этого запроса.
- Создаем индекс для ускорения поиска по embedding-вектору в колонке embedding.
- Операторный класс
vector_cosine_ops
— это способ измерения косинусного расстояния, которое показывает угол между векторами. - Сравниваем каждый вектор из таблицы items с вектором-запросом [0.1, -0.8, 0.5].
- Оператор
<->
вызывает расчет расстояния согласно выбранной метрике. - Псевдоним
AS similarity
помогает нам отобразить значение косинусной «дистанции» между векторами. - Сортируем строки от наименьшего косинусного расстояния к наибольшему. Выводим три наиболее похожие.
Результат может быть каким то таким:

Первая строка показывает точное совпадение, поэтому расстояние = 0. Остальные с минимальными отличиями, но направление у них довольно близкое к запросу.
Чтобы лучше понимать, что значит «близкое», разберем рассчет расстояния в этой метрике. Оно измеряется как косинус угла между векторами и показывает, насколько их направления совпадают:
- cos 0° (одинаковое направление) = 1,
- cos 90° (ортогональные, несвязанные векторы) = 0,
- cos 180° (противоположные направления) = -1.
При этом значение, которое возвращает оператор
<->
с vector_cosine_ops
, не является напрямую косинусом угла между векторами, а вычисляется как 1 - cos
(угла между векторами).Таким образом, максимальное отличие по косинусной метрике — это расстояние, равное 2. Оно достигается у векторов с противоположным направлением и является крайним случаем — антисмыслом, с противоположной запросу семантикой.
Скалярное произведение: vector_ip_ops
Скалярное произведение измеряет степень сонаправленности векторов. Такой тип оператора подходит в задачах рекомендаций, где важна схожесть предпочтений. Чем больше значение, тем выше степень совпадения интересов.
CREATE INDEX ON items USING ivfflat (embedding vector_ip_ops);
SELECT id, embedding, embedding <-> '[0.1, -0.8, 0.5]' AS inner_product
FROM items
ORDER BY embedding <-> '[0.1, -0.8, 0.5]'
LIMIT 3;
Уже по сложившейся традиции рассмотрим подробнее, что происходит в этом запросе.
- Снова создаем индекс по колонке embedding с использованием метода
ivfflat
и операторного классаvector_ip_ops
, определяющего метрику сравнения (в данном случае это скалярное произведение). - Извлекаем
id
и сам векторembedding
, вычисляем расстояние между ним и входным вектором [0.1, -0.8, 0.5]. - Результаты сортируются по мере убывания схожести, возвращаются только три наиболее похожих по направлению вектора.
Пример вывода:

Это может быть полезно, например, для рекомендательных систем. Если embedding пользователя и товара «смотрят» в одну сторону, то, возможно, этот товар релевантен интересам пользователя.
Чтобы избежать полного перебора, мы выше использовали индексы. После загрузки данных не забываем обновить статистику:
ANALYZE items;
Теперь можно делать запросы быстрее.
Что под капотом
Векторы хранятся как массивы
float4
и сравниваются через SIMD-инструкции. Индексы реализованы аналогично IVF в Faiss. На данный момент в pgvector нет поддержек:- HNSW (UPDATE: с версии pgvector 0.7.0 поддерживается для pg16+),
- Multi-vector запросов (например, объединение нескольких embedding'ов),
- хранения версий или тегов.
ivfflat — это Approximate Nearest Neighbor (ANN). Он не гарантирует, что ближайший найденный объект действительно окажется ближайшим по метрике. Это компромисс между скоростью и точностью.
Кроме того, есть еще пара подводных камней, которые стоит учитывать при работе с этим инструментом.
- Вывод команд недетерминирован — два запуска могут дать разные результаты.
- Индекс поддерживает только одну метрику.
- Нельзя обновить вектор в индексе — допустимы только удаление и повторная вставка.
Это может повлиять на статистику и эффективность планировщика запросов PostgreSQL. Когда вы обновляете вектор, удаляя старую строку и вставляя новую, это оставляет в таблице «мертвые» строки. PostgreSQL не удаляет их сразу — они остаются до тех пор, пока не сработает autovacuum или не выполнится вакуум инженером в ручную.
Если таких операций становится много, таблица раздуется, производительность упадет, а планировщик будет выбирать неэффективные планы из-за устаревшей статистики. Особенно это критично для больших таблиц с индексами и при профиле нагрузки, который подразумевает постоянное обновление большого количества данных. Подробнее об этом механизме я рассказывал в прошлой статье.
Вывод
pgvector — простой способ добавить векторный поиск туда, где уже есть PostgreSQL, и понять необходимость новой функциональности в вашем проекте. Он не заменит более серьезные инструменты и, скорее всего, не справится с enterprise-нагрузкой при работе с миллиардами объектов, но идеально подойдет для:
- MVP или стартапов,
- простых интеграций,
- нетребовательных внутренних AI-систем,
- pet-проектов.
Напоследок поделюсь интересным сравнением различных инструментов хранения векторных данных. Надеюсь, оно поможет вам правильно выбрать наиболее подходящий для вас.

pgvector в рейтинге инструментов хранения Vector DB.
А мы недавно выпустили ультимативный по производительности сервис — первый в России DBaaS на выделенных серверах. Подробнее об этой услуге я уже рассказывал в одной из предыдущих статей.
Обязательно делитесь вашим мнением и опытом в комментариях. В обозримом будущем я продолжу эту тему и расскажу о других полезных расширениях для PostgreSQL.
Комментарии (7)
MultiView
25.06.2025 07:42Спасибо, однако все примеры на английском языке, такое впечатление, что используются западные проекты для образца. И кстати, самый важный вопрос - сам процесс векторизации и загрузки векторов текстовых или аудио/видео совсем не раскрыт, формирование вектора на основе документов и датасетов, хотя бы размером несколько сот тысяч вопросов и ответов. Вы не указали, что PGVector не дружит нативно с open-source векторизаторами из huggingface, например популярным E5 Intfloat. Каким образом будет строиться серьезный проект на основе PGVector, ведь основная задача это быстро получить вектор и интегрировать его в Postgresql ? Мы вынуждены были отказаться от этого решения в пользу другого только из-за данных проблем.
GrishinAlex Автор
25.06.2025 07:42Спасибо за комментарий, согласен с вашим мнением. Поэтому пишу однозначно что лично я считаю что для реально большого и требовательного проекта можно посмотреть другие инструменты и предлогаю рейтинг.
Programmer74
25.06.2025 07:42Но ведь у PgVector есть поддержка HNSW, на гитхабе в ридми даже написано
https://github.com/pgvector/pgvectorGrishinAlex Автор
25.06.2025 07:42Спасибо за валидный комментарий. Вы правы. Судя по всему в мае 2024 года в pg_vector действительно появилась поддержка HNSW. И если я правильно понял только для последних версий PG (16 и 17). Еще раз спасибо, я отредактирую статью по следам нашей дискусии.
gorod0k
Спасибо. Как раз месяц назад задавался вопросом, как хранить ембеддинги лиц для кластеризации
GrishinAlex Автор
Благодарю за комментарий, буду продолжать эту тему дальше, подписывайтесь.