vector. Использование векторных типов данных#

Версия: 0.7.4

В исходном дистрибутиве установлено по умолчанию: нет.

Связанные компоненты: Отсутствуют.

Схема размещения: ext.

vector – это расширение с открытым исходным кодом для реализации векторных типов хранения данных и поиска по сходству векторов для СУБД на основе PostgreSQL. Позволяет хранить векторы вместе с остальными данными в БД.

Поддерживает:

  • хранение векторов:

    • с одинарной точностью;

    • с половинной точностью;

    • в двоичных и разреженных векторах;

  • точный и приблизительный поиск ближайших соседей для векторов по:

    • расстоянию L2;

    • внутреннему произведению;

    • косинусному расстоянию;

    • расстоянию L1;

    • расстоянию Хэмминга (только для двоичных векторов);

    • расстоянию Жаккарда (только для двоичных векторов);

Также поддерживается:

  • соответствие ACID;

  • восстановление на определенный момент времени;

  • объединения и другие функции PostgreSQL.

vector доступен для приложений на любом языке программирования с помощью клиента PostgreSQL.

Доработка#

Добавление расширения vector не проводилась.

Ограничения#

Ограничения отсутствуют.

Установка#

Расширение vector доступно для продукта Pangolin, начиная с версии 6.4.0 и не требует дополнительных действий для сборки. Для того чтобы начать использовать расширение, необходимо один раз установить его в нужную БД командой:

CREATE EXTENSION vector;

Также возможно установить расширение в конкретную схему:

CREATE EXTENSION vector SCHEMA ext;

Для дальнейшего использования расширения, во избежание проблем, схема с расширением должна быть добавлена в пути поиска для УЗ, использующей векторы в своей работе:

SET search_path=public,ext;

Использование модуля#

Быстрый старт#

Создание таблицы с вектором в трех измерениях:

CREATE TABLE items (id bigserial PRIMARY KEY, embedding vector(3));

Вставка данных:

INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]');

Поиск ближайшего соседа по расстоянию L2:

SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;

Также поддерживается:

  • внутреннее произведение <#>;

  • косинусное расстояние <=>;

  • расстояние L1 <+>.

Примечание:

<#> возвращает отрицательное внутреннее произведение, поскольку PostgreSQL поддерживает только порядок ASC для сканирования индекса по оператору.

Хранение#

Создание новой таблицы с векторным полем:

CREATE TABLE items (id bigserial PRIMARY KEY, embedding vector(3));

Также возможно добавление векторного поля к существующей таблице:

ALTER TABLE items ADD COLUMN embedding vector(3);

Также поддерживаются другие векторные типы:

  • с половинной точностью;

  • двоичные векторы;

  • разреженные векторы.

Вставка данных:

INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]');

Возможна загрузка векторов пачкой с использованием команды COPY:

COPY items (embedding) FROM STDIN WITH (FORMAT BINARY);

Добавление векторов:

INSERT INTO items (id, embedding) VALUES (1, '[1,2,3]'), (2, '[4,5,6]')
    ON CONFLICT (id) DO UPDATE SET embedding = EXCLUDED.embedding;

Обновление векторов:

UPDATE items SET embedding = '[1,2,3]' WHERE id = 1;

Удаление векторов:

DELETE FROM items WHERE id = 1;

Выполнение запросов#

Поиск ближайшего соседа для вектора:

SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;

Поддерживаются следующие функции поиска:

  • <-> - расстояние L2;

  • <#> - (негативное) внутреннее произведение;

  • <=> - косинусное расстояние;

  • <+> - расстояние L1;

  • <~> - Расстояние Хэмминга (для бинарного типа);

  • <%> - расстояние Джаккарда (для бинарного типа).

Поиск ближайшего соседа для строки:

SELECT * FROM items WHERE id != 1 ORDER BY embedding <-> (SELECT embedding FROM items WHERE id = 1) LIMIT 5;

Получение строк на определенном расстоянии:

SELECT * FROM items WHERE embedding <-> '[3,1,2]' < 5;

Примечание:

Необходимо использовать совместно с ORDER BY и LIMIT для использования индекса.

Расстояния#

Вычисление расстояний:

SELECT embedding <-> '[3,1,2]' AS distance FROM items;

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

SELECT (embedding <#> '[3,1,2]') * -1 AS inner_product FROM items;

Для косинусного расстояния необходимо результат вычисления вычитать из 1:

SELECT 1 - (embedding <=> '[3,1,2]') AS cosine_similarity FROM items;

Аггрегатные функции#

Вычисление средних значений:

SELECT AVG(embedding) FROM items;

Вычисление средних значений с группировкой:

SELECT category_id, AVG(embedding) FROM items GROUP BY category_id;

Индексирование#

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

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

Поддерживаются следующие типы индексов:

  • HNSW;

  • IVFFlat.

HNSW#

Индекс HNSW создает многослойный граф. Он обеспечивает лучшую производительность запросов, по сравнению с IVFFlat (с точки зрения соотношения скорости и результата), но медленнее строится и использует больше памяти. Кроме того, данный индекс может быть создан без каких-либо данных в таблице, поскольку в нем нет шага обучения, как в IVFFlat.

Примеры добавления индекса для каждой функции расстояния, которую возможно использовать:

расстояние L2:

CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);

Примечание:

Используйте halfvec_l2_ops для halfvec (и аналогично с другими типами и функциями расстояния).

Внутреннее произведение:

CREATE INDEX ON items USING hnsw (embedding vector_ip_ops);

Косинусное расстояние:

CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);

Расстояние L1:

CREATE INDEX ON items USING hnsw (embedding vector_l1_ops);

Расстояние Хэмминга (только для бинарного типа векторов):

CREATE INDEX ON items USING hnsw (embedding bit_hamming_ops);

Расстояние Джаккарда (только для бинарного типа векторов):

CREATE INDEX ON items USING hnsw (embedding bit_jaccard_ops);

Поддерживаемые типы для данного индекса:

  • vector - до 2000 измерений (2000-мерное пространство);

  • halfvec - до 4000 измерений (4000-мерное пространство);

  • bit - до 64000 измерений (64000-мерное пространство);

  • sparsevec - до 1000 ненулевых координат.

Опции индексирования#

Указание параметров HNSW:

  • m - максимальное количество подключений на слой (по умолчанию 16);

  • ef_construction - размер динамического списка кандидатов для построения графа (по умолчанию 64).

CREATE INDEX ON items USING hnsw (embedding vector_l2_ops) WITH (m = 16, ef_construction = 64);

Более высокое значение ef_construction обеспечивает лучшую воспроизводимость, однако страдают время построения индекса и скорость вставки.

Опции запросов#

Размер динамического списка кандидатов для поиска (по умолчанию 40):

SET hnsw.ef_search = 100;

Более высокое значение обеспечивает лучшую воспроизводимость в обмен на скорость.

Использование SET LOCAL внутри блока транзакции позволяет установить параметр для одного запроса:

BEGIN;
SET LOCAL hnsw.ef_search = 100;
SELECT ...
COMMIT;
Время построения индекса#

Индексы строятся значительно быстрее, когда граф помещается в maintenance_work_mem:

SET maintenance_work_mem = '8GB';

Когда граф больше не помещается, отображается уведомление:

NOTICE:  hnsw graph no longer fits into maintenance_work_mem after 100000 tuples
DETAIL:  Building will take significantly more time.
HINT:  Increase maintenance_work_mem to speed up builds.

Примечание:

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

Как и в случае с другими типами индексов, быстрее будет создать индекс после загрузки исходных данных. Также можно ускорить создание индекса, увеличив количество параллельных рабочих процессов (по умолчанию 2):

SET max_parallel_maintenance_workers = 7; -- plus leader

При большом количестве воркеров также может потребоваться увеличить значение max_parallel_workers (по умолчанию 8).

Отображение прогресса индексирования#

Начиная с версии ядра PostgreSQL 12+, возможно смотреть прогресс индексирования следующим запросом:

SELECT phase, round(100.0 * blocks_done / nullif(blocks_total, 0), 1) AS "%" FROM pg_stat_progress_create_index;

Построение индекса HNSW состоит из фаз:

  1. initializing;

  2. loading tuples.

IVFFlat#

Индекс IVFFlat разбивает векторы на списки, а затем выполняет поиск в подмножестве тех списков, которые ближе всего расположены к вектору запроса. Он обеспечивает более быстрое построение и использует меньше памяти, чем HNSW, но обладает более низкой производительностью запроса (с точки зрения соотношения скорости и возврата).

Вот три ключа к достижению хорошей воспроизводимости:

  • создание индекса после того, как в таблице появятся данные;

  • подходящее количество списков - лучше всего начать с rows/1000 для 1 млн строк и sqrt(rows) для более чем 1 млн строк;

  • при запросе указывать подходящее количество проб (чем больше, тем лучше для запоминания, чем меньше, тем лучше для скорости) - лучше всего начать с sqrt(lists).

Ниже приведены примеры добавления индексов для каждой функции определения расстояния, которую планируется использовать.

Расстояние L2:

CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);

Примечание:

Используйте halfvec_l2_ops для halfvec (и аналогично с другими типами и функциями расстояния).

Внутреннее произведение:

CREATE INDEX ON items USING ivfflat (embedding vector_ip_ops) WITH (lists = 100);

Косинусное расстояние:

CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);

Расстояние Хэмминга(только для бинарного типа):

CREATE INDEX ON items USING ivfflat (embedding bit_hamming_ops) WITH (lists = 100);

Поддерживаемые типы для данного индекса:

  • vector - до 2000 измерений(2000-мерное пространство);

  • halfvec - до 4000 измерений(4000-мерное пространство);

  • bit - до 64000 измерений(64000-мерное пространство).

Опции запросов#

Указание количества проб (по умолчанию 1):

SET ivfflat.probes = 10;

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

Использование SET LOCAL внутри транзакции позволяет установить параметр для одного запроса:

BEGIN;
SET LOCAL ivfflat.probes = 10;
SELECT ...
COMMIT;
Время построения индекса#

Возможно ускорить создание индекса в больших таблицах за счет увеличения числа параллельных обработчиков (по умолчанию 2):

SET max_parallel_maintenance_workers = 7; -- plus leader

При большом количестве воркеров также может потребоваться увеличить значение max_parallel_workers (по умолчанию 8).

Отображение прогресса индексирования#

Начиная с версии ядра PostgreSQL 12+ возможно смотреть прогресс индексирования следующим запросом:

SELECT phase, round(100.0 * blocks_done / nullif(blocks_total, 0), 1) AS "%" FROM pg_stat_progress_create_index;

Фазы построения индекса IVFFlat:

  1. initializing;

  2. performing k-means;

  3. assigning tuples;

  4. loading tuples.

Примечание:

Фильтрация#

Существует несколько способов индексировать запросы ближайшего соседа с помощью предложения WHERE:

SELECT * FROM items WHERE category_id = 123 ORDER BY embedding <-> '[3,1,2]' LIMIT 5;

Создание индекса по одному или нескольким столбцам, используемым в директиве WHERE для точного поиска:

CREATE INDEX ON items (category_id);

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

CREATE INDEX ON items USING hnsw (embedding vector_l2_ops) WHERE (category_id = 123);

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

CREATE TABLE items (embedding vector(3), category_id int) PARTITION BY LIST(category_id);

Типы векторов#

Векторы с половинной точностью#

Для хранения векторов с половинной точностью используется тип halfvec:

CREATE TABLE items (id bigserial PRIMARY KEY, embedding halfvec(3));

Индекс вектора с половинной точностью, для уменьшения размера индекса:

CREATE INDEX ON items USING hnsw ((embedding::halfvec(3)) halfvec_l2_ops);

Получение ближайшего соседа:

SELECT * FROM items ORDER BY embedding::halfvec(3) <-> '[1,2,3]' LIMIT 5;

Двоичные векторы#

Для хранения векторов в двоичном формате используется тип bit:

CREATE TABLE items (id bigserial PRIMARY KEY, embedding bit(3));
INSERT INTO items (embedding) VALUES ('000'), ('111');

Получение ближайшего соседа по расстоянию Хэмминга:

SELECT * FROM items ORDER BY embedding <~> '101' LIMIT 5;

Еще один способ:

SELECT * FROM items ORDER BY bit_count(embedding # '101') LIMIT 5;

Также поддерживается поиск по расстоянию Джаккарада (<%>).

Двоичное квантование#

Используйте выражение индексации для двоичного квантования:

CREATE INDEX ON items USING hnsw ((binary_quantize(embedding)::bit(3)) bit_hamming_ops);

Получение ближайшего соседа по расстоянию Хэмминга:

SELECT * FROM items ORDER BY binary_quantize(embedding)::bit(3) <~> binary_quantize('[1,-2,3]') LIMIT 5;

Переранжирование по исходным векторам для лучшей воспроизводимости:

SELECT * FROM (
    SELECT * FROM items ORDER BY binary_quantize(embedding)::bit(3) <~> binary_quantize('[1,-2,3]') LIMIT 20
) ORDER BY embedding <=> '[1,-2,3]' LIMIT 5;

Разреженные векторы#

Для хранения разреженных векторов используется тип sparsevec:

CREATE TABLE items (id bigserial PRIMARY KEY, embedding sparsevec(5));

Вставка векторов:

INSERT INTO items (embedding) VALUES ('{1:1,3:2,5:3}/5'), ('{1:4,3:5,5:6}/5');

Разреженные векторы хранятся в формате {index1:value1,index2:value2}/dimensions и отсчет начинается с 1 аналогично SQL-массивам. Данный формат векторов нужен, чтобы не тратить место на диске для хранения нулей.

Получение ближайшего соседа по расстоянию L2:

SELECT * FROM items ORDER BY embedding <-> '{1:3,3:1,5:2}/5' LIMIT 5;

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

Использование совместно с полнотекстовым поиском PostgreSQL для гибридного поиска:

SELECT id, content FROM items, plainto_tsquery('hello search') query
    WHERE textsearch @@ query ORDER BY ts_rank_cd(textsearch, query) DESC LIMIT 5;

Возможно использовать взаимное ранжирование или перекрестный кодер для объединения результатов.

Индексирование сабвекторов#

Использование выражения индексирования для индексирования сабвекторов:

CREATE INDEX ON items USING hnsw ((subvector(embedding, 1, 3)::vector(3)) vector_cosine_ops);

Получение ближайшего соседа по косинусному расстоянию:

SELECT * FROM items ORDER BY subvector(embedding, 1, 3)::vector(3) <=> subvector('[1,2,3,4,5]'::vector, 1, 3) LIMIT 5;

Переранжирование по исходным векторам для лучшей воспроизводимости:

SELECT * FROM (
    SELECT * FROM items ORDER BY subvector(embedding, 1, 3)::vector(3) <=> subvector('[1,2,3,4,5]'::vector, 1, 3) LIMIT 20
) ORDER BY embedding <=> '[1,2,3,4,5]' LIMIT 5;

Производительность#

Тюнинг#

Используйте инструмент, подобный PgTune, для установки начальных значений параметров сервера PostgreSQL. Например, shared_buffers обычно должны занимать 25% памяти сервера. Можно найти конфигурационный файл с помощью команды:

SHOW config_file;

И проверить конкретную настройку:

SHOW shared_buffers;

Иногда необходимо рестартовать PostgreSQL для применения настройки.

Загрузка данных#

Использование команды COPY для загрузки данных порциями (пример):

COPY items (embedding) FROM STDIN WITH (FORMAT BINARY);

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

Индексирование#

Обратите внимание на время построения индексов HNSW и IVFFlat.

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

CREATE INDEX CONCURRENTLY ...

Выполнение запросов#

Используйте EXPLAIN ANALYZE чтобы оценить производительность. Например:

EXPLAIN ANALYZE SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;

Точный поиск#

Чтобы ускорить выполнение запросов без индекса, увеличьте значение max_parallel_workers_per_gather:

SET max_parallel_workers_per_gather = 4;

Если векторы нормализованы по длине (как эмбеддинги в OpenAI), используйте внутреннее произведение для достижения наилучшей производительности:

SELECT * FROM items ORDER BY embedding <#> '[3,1,2]' LIMIT 5;

Приблизительный поиск#

Чтобы ускорить выполнение запросов с индексом IVFFlat, увеличьте количество инвертированных списков (за счет воспроизводимости):

CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 1000);

Очистка#

Очистка может ожидать индексы HNSW. Для ускорения процесса вакуума имеет смысл сперва выполнить REINDEX:

REINDEX INDEX CONCURRENTLY index_name;
VACUUM table_name;

Мониторинг#

Контролировать производительность возможно с помощью pg_stat_statements (обязательно добавьте его в shared_preload_libraries):

CREATE EXTENSION pg_stat_statements;

Получение запросов, которые выполнялись дольше всего:

SELECT query, calls, ROUND((total_plan_time + total_exec_time) / calls) AS avg_time_ms,
    ROUND((total_plan_time + total_exec_time) / 60000) AS total_time_min
    FROM pg_stat_statements ORDER BY total_plan_time + total_exec_time DESC LIMIT 20;

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

BEGIN;
SET LOCAL enable_indexscan = off; -- use exact search
SELECT ...
COMMIT;

Также возможно использование инструментов накопления статистики, таких как pg_profile или performance_insight.

Масштабирование#

vector масштабируется также, как PostgreSQL.

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

Горизонтальное масштабирование достигается с помощью реплик или специальных библиотек(например, Citus) или другого подхода для сегментирования (пример - https://github.com/pgvector/pgvector-python/blob/master/examples/citus/example.py).

Языки программирования#

Используйте vector из различных языков программирования через драйвер PostgreSQL. Возможно даже генерировать и сохранять вектора из приложения на одном языке, а использовать из другого.

Язык

Модуль

Ссылка

C

pgvector-c

https://github.com/pgvector/pgvector-c

C++ pgvector-cpp

https://github.com/pgvector/pgvector-cpp

C#, F#, Visual Basic

pgvector-dotnet

https://github.com/pgvector/pgvector-dotnet

Crystal

pgvector-crystal

https://github.com/pgvector/pgvector-crystal

Dart

pgvector-dart

https://github.com/pgvector/pgvector-dart

Elixir

pgvector-elixir

https://github.com/pgvector/pgvector-elixir

Go

pgvector-go

https://github.com/pgvector/pgvector-go

Haskell

pgvector-haskell

https://github.com/pgvector/pgvector-haskell

Java, Kotlin, Groovy, Scala

pgvector-java

https://github.com/pgvector/pgvector-java

JavaScript, TypeScript

pgvector-node

https://github.com/pgvector/pgvector-node

Julia

pgvector-julia

https://github.com/pgvector/pgvector-julia

Lisp

pgvector-lisp

https://github.com/pgvector/pgvector-lisp

Lua

pgvector-lua

https://github.com/pgvector/pgvector-lua

Nim

pgvector-nim

https://github.com/pgvector/pgvector-nim

OCaml

pgvector-ocaml

https://github.com/pgvector/pgvector-ocaml

Perl

pgvector-perl

https://github.com/pgvector/pgvector-perl

PHP

pgvector-php

https://github.com/pgvector/pgvector-php

Python

pgvector-python

https://github.com/pgvector/pgvector-python

R

pgvector-r

https://github.com/pgvector/pgvector-r

Ruby

Neighbor

https://github.com/ankane/neighbor

Ruby

pgvector-ruby

https://github.com/pgvector/pgvector-ruby

Rust

pgvector-rust

https://github.com/pgvector/pgvector-rust

Swift

pgvector-swift

https://github.com/pgvector/pgvector-swift

Zig

pgvector-zig

https://github.com/pgvector/pgvector-zig

Часто задаваемые вопросы#

Как много векторов может быть сохранено в одной таблице?

Непартиционированные таблицы имеют ограничение 32 TB по умолчанию в PostgreSQL. Партиционированные таблицы могут иметь тысячи партиций такого размера.

Поддерживается ли репликация?

Да, pgvector использует журнал предзаписи (WAL), который позволяет выполнть репликацию и восстановление на точку во времени.

Что делать, если я хочу индексировать векторы с более чем 2000 измерений?

Возможно использовать векторы с половинной точностью для увеличения до 4,000 измерений или бинарное квантование для увеличения до 64,000 измерений. Другой вариант - уменьшение размерности вектора.

Могу ли я сохранять векторы различной размерности в одной колонке?

Можно использовать тип без размерности, например vector вместо vector(3):

CREATE TABLE embeddings (model_id bigint, item_id bigint, embedding vector, PRIMARY KEY (model_id, item_id));

Однако использовать индекс возможно только на строках с одинаковой размерностью векторов (используйте условия и частичное индексирование):

CREATE INDEX ON embeddings USING hnsw ((embedding::vector(3)) vector_l2_ops) WHERE (model_id = 123);

И пример выполнения такого запроса:

SELECT * FROM embeddings WHERE model_id = 123 ORDER BY embedding::vector(3) <-> '[3,1,2]' LIMIT 5;

Могу ли я хранить векторы с большей точностью?

Возможно использование типов double precision[] или numeric[] для сохранения векторов с большей точностью:

CREATE TABLE items (id bigserial PRIMARY KEY, embedding double precision[]);

-- use {} instead of [] for Postgres arrays
INSERT INTO items (embedding) VALUES ('{1,2,3}'), ('{4,5,6}');

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

ALTER TABLE items ADD CHECK (vector_dims(embedding::vector) = 3);

Используйте индексирование по условию (для небольших размерностей):

CREATE INDEX ON items USING hnsw ((embedding::vector(3)) vector_l2_ops);

и выполняйте запросы:

SELECT * FROM items ORDER BY embedding::vector(3) <-> '[3,1,2]' LIMIT 5;

Должен ли индекс умещаться в памяти?

Нет, но, как и в случае с другими типами индексов, в этом случае производительность, скорее всего, повысится. Можно получить размер индекса с помощью:

SELECT pg_size_pretty(pg_relation_size('index_name'));

Почему запрос не использует индексы?

Запрос должен содержать ORDER BY и LIMIT, а ORDER BY должен быть результатом выполнения оператора вычисления дистанции (не выражения) в порядке ASC:

-- index
ORDER BY embedding <=> '[3,1,2]' LIMIT 5;
 
-- no index
ORDER BY 1 - (embedding <=> '[3,1,2]') DESC LIMIT 5;

Можно предложить планировщику использовать индекс для запроса с помощью:

BEGIN;
SET LOCAL enable_seqscan = off;
SELECT ...
COMMIT;

Также, если таблица имеет небольшие размеры, полное сканирование может быть быстрее.

Почему в запросе не используется параллельное сканирование таблицы?

Планировщик не учитывает внешнее хранение (TOAST) в оценке стоимости, что может удешевлять последовательное сканирование. Можно снизить стоимость параллельного сканирования запроса с помощью:

BEGIN;
SET LOCAL min_parallel_table_scan_size = 1;
SET LOCAL parallel_setup_cost = 1;
SELECT ...
COMMIT;

Или выберите встроенное хранение векторов:

ALTER TABLE items ALTER COLUMN embedding SET STORAGE PLAIN;

Почему после добавления индекса HNSW результатов по запросу становится меньше?

Результаты ограничены размером динамического списка кандидатов (hnsw.ef_search). Результатов может быть еще меньше из-за неактивных кортежей или условий фильтрации в запросе. Мы рекомендуем задать для параметра hnsw.ef_search значение, как минимум в два раза превышающее значение LIMIT в запросе. Если нужно более 500 результатов, лучше использовать индекс IVFFlat.

Также обратите внимание, что векторы со значением NULL не индексируются (как и нулевые векторы для косинусного расстояния).

Почему после добавления индекса IVFFlat результатов по запросу становится меньше?

Вероятно, индекс был создан с использованием слишком малого количества данных для такого количества списков. Удалите индекс до тех пор, пока в таблице не будет больше данных.

DROP INDEX index_name;

Результаты также могут быть ограничены количеством проб (параметр ivfflat.probes).

Также стоить иметь в виду, что векторы со значением NULL не индексируются (так же, как нулевые векторы для косинусного расстояния).

Техническое описание типов векторов#

Тип vector#

Каждый вектор занимает (4 * размерность + 8) байт в памяти. Каждый элемент представляет собой число с плавающей запятой одинарной точности (как тип real в Postgres), и все элементы должны быть конечными (без NaN, Infinity или -Infinity). Векторы могут иметь до 16 000 измерений.

Доступные операторы:

  • + - поэлементное сложение;

  • - - поэлементное вычитание;

  • * - поэлементное умножение;

  • || - конкатенация;

  • <-> - Евклидово расстояние(расстояние L2);

  • <#> - отрицательное внутреннее произведение;

  • <=> - косинусное расстояние;

  • <+> - расстояние городских кварталов(расстояние L1).

Доступные функции:

  • binary_quantize(vector) RETURN bit - бинарное квантование;

  • cosine_distance(vector, vector) RETURN double precision - косинусное расстояние;

  • inner_product(vector, vector) RETURN double precision - внутреннее произведение;

  • l1_distance(vector, vector) RETURN double precision - расстояние L1;

  • l2_distance(vector, vector) RETURN double precision - расстояние L2;

  • l2_normalize(vector) RETURN vector - нормализация по Евклиду;

  • subvector(vector, integer, integer) RETURN vector - получение сабвектора;

  • vector_dims(vector) RETURN integer - размерность вектора;

  • vector_norm(vector) RETURN double precision - Евклидова норма.

Доступные агрегатные функции:

  • avg(vector) RETURN vector - среднее;

  • sum(vector) RETURN vector - сумма.

Тип halfvec#

Каждый вектор с половинной точностью занимает (2 * размерность + 8) байт в памяти. Каждый элемент представляет собой число с плавающей запятой половинной точности, и все элементы должны быть конечными (без NaN, Infinity или -Infinity). Векторы с половинной точностью могут иметь до 16 000 измерений.

Доступные операторы:

  • + - поэлементное сложение;

  • - - поэлементное вычитание;

  • * - поэлементное умножение;

  • || - конкатенация;

  • <-> - Евклидово расстояние(расстояние L2);

  • <#> - отрицательное внутреннее произведение;

  • <=> - косинусное расстояние;

  • <+> - расстояние городских кварталов(расстояние L1).

Доступные функции:

  • binary_quantize(halfvec) RETURN bit - бинарное квантование;

  • cosine_distance(halfvec, halfvec) RETURN double precision - косинусное расстояние;

  • inner_product(halfvec, halfvec) RETURN double precision - внутреннее произведение;

  • l1_distance(halfvec, halfvec) RETURN double precision - расстояние L1;

  • l2_distance(halfvec, halfvec) RETURN double precision - расстояние L2;

  • l2_normalize(halfvec) RETURN halfvec - нормализация по Евклиду;

  • subvector(halfvec, integer, integer) RETURN halfvec - получение сабвектора;

  • vector_dims(halfvec) RETURN integer - размерность вектора;

  • l2_norm(halfvec) RETURN double precision - Евклидова норма.

Доступные агрегатные функции:

  • avg(halfvec) RETURN halfvec - среднее;

  • sum(halfvec) RETURN halfvec - сумма.

Тип bit#

Каждый бинарный вектор занимает (размерность/8 + 8) байт в памяти. Смотрите документацию на стандартный тип bit https://www.postgresql.org/docs/current/datatype-bit.html для получения подробностей.

Доступные операторы:

  • <~> - расстояние Хэмминга;

  • <%> - расстояние Джаккарда.

Доступные функции:

  • hamming_distance(bit, bit) RETURN double precision - расстояние Хэмминга;

  • jaccard_distance(bit, bit) RETURN double precision - расстояние Джаккарда.

Тип sparsevec#

Каждый разреженный вектор занимает (8 * количество ненулевых элементов + 16) байт в памяти. Каждый элемент представляет собой число с плавающей запятой одинарной точности, и все элементы должны быть конечными (без NaN, Infinity или -Infinity). Разреженные векторы могут содержать до 16 000 ненулевых элементов.

Доступные операторы:

  • <-> - Евклидово расстояние(расстояние L2);

  • <#> - отрицательное внутреннее произведение;

  • <=> - косинусное расстояние;

  • <+> - расстояние городских кварталов(расстояние L1).

Доступные функции:

  • cosine_distance(sparsevec, sparsevec) RETURN double precision - косинусное расстояние;

  • inner_product(sparsevec, sparsevec) RETURN double precision - внутреннее произведение;

  • l1_distance(sparsevec, sparsevec) RETURN double precision - расстояние L1;

  • l2_distance(sparsevec, sparsevec) RETURN double precision - расстояние L2;

  • l2_normalize(sparsevec) RETURN sparsevec - нормализация по Евклиду;

  • l2_norm(sparsevec) RETURN double precision - Евклидова норма.

Ссылки на документацию разработчика#

Дополнительно поставляемый модуль vector: https://github.com/pgvector/pgvector.