- PVSM.RU - https://www.pvsm.ru -

Итак, ваш проект вырос и вам потребовалась новая функциональность, будь то рекомендательный движок, база знаний или автоматизированная первая линия техподдержки. Для всего этого можно использовать векторный и/или семантический поиск, а также интегрировать в проект LLM. Поздравляю — теперь вам нужно еще и хранить embedding-векторы, а также искать по ним ближайшие объекты. Решений два: внешняя векторная БД или интеграция всего этого богатства в существующий стек. Второй путь проще на старте, немного быстрее и обычно дешевле — разумеется, если вы уже используете PostgreSQL.
Привет! Меня зовут Александр Гришин, я отвечаю за развитие продуктов хранения данных в Selectel: облачных баз данных [1] и S3-хранилища [2]. В этой статье я расскажу о pgvector — расширении для PostgreSQL, которое позволяет добавить векторный поиск без внешних сервисов, пересборки архитектуры и большого количества работы. Материал пригодится продуктовым командам, архитекторам, бэкенд-разработчикам и инженерам данных.
Если вы это читаете, то, скорее всего, уже используете PostgreSQL. Возможно, вы не хотите сразу разворачивать отдельную инфраструктуру под векторный поиск, планируете попробовать работу с embedding'ами от OpenAI, Cohere, HuggingFace и других моделей, планируете строить или уже строите RAG-системы, рекомендательные движки, семантический поиск или базу знаний в MVP-формате.
Используйте навигацию, если не хотите читать текст целиком:
→ Зачем вообще нужен векторный поиск [3]
→ Что такое pgvector [4]
→ Развернем pgvector в облачной базе данных [5]
→ Рассмотрим расширение детальнее [6]
→ Что под капотом [7]
→ Вывод [8]
Когда вы работаете с embedding'ами от LLM, вы неизбежно сталкиваетесь со следующей задачей. Приходит запрос клиента и нужно быстро находить похожие объекты по вектору.
Вот типовые примеры таких задач.
Индустрия предлагает широкий спектр специализированных инфраструктурных решений для таких задач: Faiss, Milvus, Pinecone, Qdrant, Weaviate, Vespa. Но так как я люблю PostgreSQL, то не могу не рассказать о возможной альтернативе на ее базе. Это дает возможность пощупать новое без зоопарка технологий и разрозненных API — прямо в PostgreSQL.

Итак, pgvector [9] — это расширение к PostgreSQL, которое добавляет тип данных vector и операторы поиска по расстоянию. Поддерживаются три основные метрики сравнения векторов.
Выбор метрики влияет на операторный класс индекса и характер сортировки поблизости. Важно понимать, какая из них подходит под вашу задачу. Например, cosine similarity чаще используется в задачах семантического поиска, а inner product может быть полезен в рекомендательных системах.
В облачном PostgreSQL [10] от Selectel pgvector можно установить в пару кликов через панель управления. С полным списком поддерживаемых расширений можно ознакомиться в документации [11].
1. Разверните кластер в панели управления [12]. Как это сделать, подробно описано в документации [13].

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

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

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

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

Когда вы передаете текст в модель вроде text-embedding-3-small [14] от 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;
Здесь оператор <-> вычисляет расстояние между двумя векторами.

Из вывода видно, что:
Таким образом, мы можем оценивать степень семантической близости любых объектов, представленных векторами. Какая именно метрика используется, зависит от операторного класса, заданного при создании индекса.
Как я уже говорил, в pgvector доступны три варианта. Рассмотрим каждый из них подробнее.
Стандартная метрика, измеряющая «прямое» расстояние между точками в пространстве. Хорошо подходит, если векторы не нормализованы.
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] в евклидовом пространстве. Чем меньше значение, тем ближе векторы.
Этот операторный класс измеряет угол между векторами. Тут получается, что важно не расстояние, а направление. Подходит для 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;
Разберем подробнее работу этого запроса.
vector_cosine_ops — это способ измерения косинусного расстояния, которое показывает угол между векторами.<-> вызывает расчет расстояния согласно выбранной метрике.AS similarity помогает нам отобразить значение косинусной «дистанции» между векторами.Результат может быть каким то таким:

Первая строка показывает точное совпадение, поэтому расстояние = 0. Остальные с минимальными отличиями, но направление у них довольно близкое к запросу.
Чтобы лучше понимать, что значит «близкое», разберем рассчет расстояния в этой метрике. Оно измеряется как косинус угла между векторами и показывает, насколько их направления совпадают:
При этом значение, которое возвращает оператор <-> с vector_cosine_ops, не является напрямую косинусом угла между векторами, а вычисляется как 1 - cos (угла между векторами).
Таким образом, максимальное отличие по косинусной метрике — это расстояние, равное 2. Оно достигается у векторов с противоположным направлением и является крайним случаем — антисмыслом, с противоположной запросу семантикой.
Скалярное произведение измеряет степень сонаправленности векторов. Такой тип оператора подходит в задачах рекомендаций, где важна схожесть предпочтений. Чем больше значение, тем выше степень совпадения интересов.
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;
Уже по сложившейся традиции рассмотрим подробнее, что происходит в этом запросе.
ivfflat и операторного класса vector_ip_ops, определяющего метрику сравнения (в данном случае это скалярное произведение).id и сам вектор embedding, вычисляем расстояние между ним и входным вектором [0.1, -0.8, 0.5].Пример вывода:

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

pgvector в рейтинге инструментов хранения Vector DB.
А мы недавно выпустили ультимативный по производительности сервис — первый в России DBaaS на выделенных серверах [18]. Подробнее об этой услуге я уже рассказывал в одной из предыдущих статей [19].
Обязательно делитесь вашим мнением и опытом в комментариях. В обозримом будущем я продолжу эту тему и расскажу о других полезных расширениях для PostgreSQL.
Автор: GrishinAlex
Источник [20]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/postgresql/423584
Ссылки в тексте:
[1] облачных баз данных: https://selectel.ru/services/cloud/managed-databases/?utm_source=habr.com&utm_medium=referral&utm_campaign=dbaas_article_pgvector_240625_content
[2] S3-хранилища: https://selectel.ru/services/cloud/storage/?utm_source=habr.com&utm_medium=referral&utm_campaign=storage_article_pgvector_240625_content
[3] Зачем вообще нужен векторный поиск: #1
[4] Что такое pgvector: #2
[5] Развернем pgvector в облачной базе данных : #3
[6] Рассмотрим расширение детальнее: #4
[7] Что под капотом: #5
[8] Вывод: #6
[9] pgvector: https://github.com/pgvector/pgvector
[10] В облачном PostgreSQL: https://selectel.ru/services/cloud/managed-databases/postgresql/?utm_source=habr.com&utm_medium=referral&utm_campaign=dbaas_article_pgvector_240625_content
[11] в документации: https://docs.selectel.ru/managed-databases/postgresql/add-extensions/#extension-descriptions
[12] в панели управления: https://my.selectel.ru/?utm_source=habr.com&utm_medium=referral&utm_campaign=myselectel_article_pgvector_240625_content
[13] в документации: https://docs.selectel.ru/managed-databases/postgresql/create-cluster/
[14] text-embedding-3-small: https://platform.openai.com/docs/guides/embeddings
[15] SIMD-инструкции: https://en.wikipedia.org/wiki/Single_instruction,_multiple_data
[16] в прошлой статье.: https://habr.com/ru/companies/selectel/articles/913572/
[17] поделюсь интересным сравнением: https://superlinked.com/vector-db-comparison
[18] DBaaS на выделенных серверах: https://selectel.ru/services/cloud/managed-databases/vdedicated/?utm_source=habr.com&utm_medium=referral&utm_campaign=dbaas_article_pgvector_240625_content
[19] в одной из предыдущих статей: https://habr.com/ru/companies/selectel/articles/880338/
[20] Источник: https://habr.com/ru/companies/selectel/articles/920824/?utm_source=habrahabr&utm_medium=rss&utm_campaign=920824
Нажмите здесь для печати.