Перейти к содержанию

Индекс HNSW и ограничения фильтрации

До этого момента мы разбирали смысл расстояния и объединение выдач. Теперь рассмотрим индекс HNSW, который pgvector использует для приближённого поиска соседей. Он особенно интересен при большом корпусе, когда сравнение вопроса со всеми векторами становится дорогим. Наши три документа не подтверждают такого выигрыша: они нужны для изучения устройства и ограничений.

Состояние lesson-08 добавляет hnsw.sql. Применение индекса, планы запросов и замеры скорости автором не выполнялись. После самостоятельной подготовки читатель должен понимать, чем выбранный индекс отличается от точного поиска, как его фильтрация влияет на количество результатов и какие наблюдения нужны до вывода об ускорении.

Что меняет приближённый индекс

Точный поиск может вычислить расстояние до каждой допустимой строки и отсортировать весь набор. HNSW использует граф соседства, чтобы исследовать только часть пространства и получить близких кандидатов. Сокращение работы достигается ценой другого договора полноты: найденный набор не обязан совпасть с точным результатом для каждого вопроса.

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

Выберем класс операций vector_cosine_ops, соответствующий оператору <=>. Класс для другой метрики не становится подходящим из-за того, что все поля имеют тип vector. Схема запросов и устройство индекса должны описывать один способ сравнения.

CREATE INDEX IF NOT EXISTS chunks_embedding_hnsw ON chunks USING hnsw(embedding vector_cosine_ops);
-- Apply locally inside the same search transaction if an HNSW plan is selected.
-- SET LOCAL hnsw.iterative_scan = strict_order;
-- EXPLAIN does not execute; EXPLAIN ANALYZE executes the query.

Когда PostgreSQL использует индекс

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

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

Для изучения плана существует EXPLAIN; он показывает выбранный способ без выполнения самого запроса. EXPLAIN ANALYZE действительно выполняет запрос и собирает наблюдения. В этой авторской подготовке не выполнялся ни один вариант. Не копируйте чужие числа времени в отчёт своей лаборатории.

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

Почему фильтр сокращает выдачу

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

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

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

Итеративное чтение кандидатов

pgvector поддерживает итеративные сканирования HNSW. Они позволяют продолжать поиск, когда после фильтрации результатов недостаточно, до достижения настроенных ограничений. В файле показана строка SET LOCAL hnsw.iterative_scan = strict_order, которую при необходимости применяют внутри той же транзакции чтения.

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

Название strict_order описывает порядок возвращённых кандидатов по расстоянию. Оно не превращает приближённый алгоритм в полный перебор всех векторов. Даже упорядоченный результат может отличаться составом от точной выдачи. Сравнивайте оба свойства отдельно: порядок и полноту.

Договор сравнения с точным поиском

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

Сохраняйте также план запроса. Если PostgreSQL использовал полный перебор в обоих вариантах, нельзя приписывать различие HNSW только потому, что индекс присутствовал в базе. Условия опыта должны подтверждать, какой механизм действительно участвовал в обработке.

Не сравнивайте измерения после изменения текста или модели. Иначе изменится не только алгоритм поиска, но и сами данные. Утверждение «стало быстрее» потеряет однозначный предмет. Отдельный снимок позволяет вернуться к прежнему случаю и объяснить, что именно было изменено.

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

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

Исходники и документация

Оглавление серии · Все состояния проекта. Для этого урока откройте папку lesson-08.

pgvector, транзакции Psycopg, Ollama Embed, структурированные ответы.

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