Перейти к основному содержимому

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. Возможно даже генерировать и сохранять вектора из приложения на одном языке, а использовать из другого.

ЯзыкМодульСсылка
Cpgvector-chttps://github.com/pgvector/pgvector-c
C++ pgvector-cpphttps://github.com/pgvector/pgvector-cpp
C#, F#, Visual Basicpgvector-dotnethttps://github.com/pgvector/pgvector-dotnet
Crystalpgvector-crystalhttps://github.com/pgvector/pgvector-crystal
Dartpgvector-darthttps://github.com/pgvector/pgvector-dart
Elixirpgvector-elixirhttps://github.com/pgvector/pgvector-elixir
Gopgvector-gohttps://github.com/pgvector/pgvector-go
Haskellpgvector-haskellhttps://github.com/pgvector/pgvector-haskell
Java, Kotlin, Groovy, Scalapgvector-javahttps://github.com/pgvector/pgvector-java
JavaScript, TypeScriptpgvector-nodehttps://github.com/pgvector/pgvector-node
Juliapgvector-juliahttps://github.com/pgvector/pgvector-julia
Lisppgvector-lisphttps://github.com/pgvector/pgvector-lisp
Luapgvector-luahttps://github.com/pgvector/pgvector-lua
Nimpgvector-nimhttps://github.com/pgvector/pgvector-nim
OCamlpgvector-ocamlhttps://github.com/pgvector/pgvector-ocaml
Perlpgvector-perlhttps://github.com/pgvector/pgvector-perl
PHPpgvector-phphttps://github.com/pgvector/pgvector-php
Pythonpgvector-pythonhttps://github.com/pgvector/pgvector-python
Rpgvector-rhttps://github.com/pgvector/pgvector-r
RubyNeighborhttps://github.com/ankane/neighbor
Rubypgvector-rubyhttps://github.com/pgvector/pgvector-ruby
Rustpgvector-rusthttps://github.com/pgvector/pgvector-rust
Swiftpgvector-swifthttps://github.com/pgvector/pgvector-swift
Zigpgvector-zighttps://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.