1. Архитектура Qdrant: коллекции, точки и механизмы индексирования векторов
Архитектура Qdrant: коллекции, точки и механизмы индексирования векторов
В предыдущем курсе мы генерировали 384-мерные эмбеддинги с помощью локальной модели all-MiniLM-L6-v2. Сохранение таких векторов в PostgreSQL через расширение pgvector отлично работает на этапе прототипа. Однако при масштабировании мульти-агентной системы до миллионов документов строгие транзакционные гарантии (ACID) реляционной базы данных становятся узким местом. Семантический поиск требует совершенно иных структур данных в оперативной памяти и на диске. Qdrant жертвует реляционной гибкостью ради ультимативной скорости вычисления расстояний в многомерных пространствах.
Иерархия данных: Коллекции и Точки
В основе Qdrant лежит двухуровневая логическая модель: коллекции (Collections) и точки (Points). Здесь нет таблиц, схем и внешних ключей.
Коллекция в Qdrant — это изолированное пространство имен, объединенное строгими математическими правилами. В отличие от таблиц PostgreSQL, где колонки могут содержать NULL или данные разной длины, коллекция Qdrant требует жесткой фиксации двух параметров при создании: размерности вектора (Vector Size) и метрики расстояния (Distance Metric). Невозможно поместить 384-мерный вектор от Sentence Transformers и 1536-мерный вектор от OpenAI в одну стандартную коллекцию — математика вычисления расстояний для них несовместима.
Базовой единицей данных внутри коллекции является Точка (Point).
!Анатомия Point в Qdrant: связь идентификатора, вектора и метаданных
Point состоит из трех обязательных компонентов:
JSONB в PostgreSQL, Payload в Qdrant не предназначен для хранения тяжелых текстов. Его главная задача — хранить легковесные теги (ID пользователя, дата создания, категория) для жесткой пре-фильтрации перед векторным поиском.Физическое хранение: сегменты и оптимизатор
Прямая вставка новых векторов в глобальный граф HNSW (Hierarchical Navigable Small World) невозможна с точки зрения производительности. Перестроение связей в графе при каждой записи полностью заблокировало бы базу данных. Qdrant решает эту проблему через архитектуру независимых сегментов.
Сегмент — это физический контейнер на диске и в памяти, хранящий определенное подмножество точек коллекции. Каждый сегмент функционирует как миниатюрная независимая векторная база данных со своим собственным индексом.
При поступлении нового запроса на запись (Upsert), данные попадают в добавляемый (appendable) сегмент. На этом этапе векторы просто складываются в линейный массив в оперативной памяти без построения HNSW-графа. Поиск по такому сегменту выполняется полным сканированием (Sequential Scan). Поскольку добавляемый сегмент мал, линейный поиск по нему происходит за доли миллисекунды и не влияет на общую задержку.
По мере накопления данных в работу включается фоновый процесс — Оптимизатор (Optimizer).
!Процесс работы оптимизатора: слияние мелких сегментов и построение HNSW-графа
Оптимизатор выполняет две критические задачи:
Как только новый проиндексированный сегмент готов, старые мелкие сегменты удаляются. Эта архитектура напоминает механизм LSM-деревьев (Log-Structured Merge-Tree), но адаптирована под специфику многомерных графов. При выполнении поискового запроса Qdrant параллельно опрашивает все существующие сегменты (и проиндексированные, и линейные), а затем сливает результаты, выдавая итоговый топ-K ответов.
Механизмы хранения: InMemory против Mmap
В предыдущем курсе при разборе формата GGUF для локальных LLM мы познакомились с системным вызовом mmap (Memory-mapped file). Qdrant использует ровно ту же технологию операционной системы для управления памятью, предлагая два режима хранения векторов:
Для HNSW-индекса режим Mmap критически важен. Навигация по графу HNSW требует случайного доступа к памяти (Random Access). Современные NVMe SSD обладают достаточной скоростью случайного чтения, чтобы Qdrant мог эффективно обходить граф, физически лежащий на диске, загружая в RAM только те узлы, которые пересекаются поисковым запросом. Это позволяет хранить миллиарды векторов на дешевых дисках, используя RAM только для кэширования горячих путей поиска.
Оптимизация метрик расстояния
При создании коллекции мы обязаны указать метрику расстояния. Векторные модели, такие как all-MiniLM-L6-v2, обучаются с использованием косинусного расстояния (Cosine Distance), которое измеряет угол между векторами, игнорируя их длину.
Формула косинусного сходства:
Где — скалярное произведение (Dot Product) векторов, а и — их длины (L2-нормы). Операция вычисления длины вектора и последующего деления требует значительных процессорных ресурсов (извлечение квадратного корня), особенно когда она выполняется миллионы раз в секунду при обходе HNSW-графа.
Здесь кроется важная архитектурная оптимизация. Библиотека sentence-transformers по умолчанию выполняет L2-нормализацию векторов перед их выдачей. Это означает, что длина каждого сгенерированного вектора принудительно приравнивается к единице: и .
Если подставить единицы в знаменатель, формула схлопывается:
Для нормализованных векторов косинусное сходство математически эквивалентно скалярному произведению. Поэтому при проектировании коллекций в Qdrant для моделей Sentence Transformers архитектурно правильно указывать метрику Dot (Dot Product), а не Cosine. Qdrant пропустит этап вычисления длин векторов на аппаратном уровне ядра процессора, что повысит общую пропускную способность (TPS) векторного поиска на 15-20% без единой потери в качестве ранжирования.
Параметры HNSW на уровне коллекции
В отличие от PostgreSQL, где параметры индексов задаются глобально или при создании конкретного индекса через DDL-запросы, Qdrant инкапсулирует конфигурацию HNSW внутри настроек коллекции. Это позволяет агентам иметь разные стратегии поиска.
Ключевые параметры, которые мы передаем при инициализации:
m (по умолчанию 16) — количество связей (ребер), которые создаются для каждого нового узла (вектора) на нижнем слое графа. Увеличение m до 32 или 64 имеет смысл для высокомерных векторов (например, 1536 от OpenAI), так как это повышает вероятность нахождения правильного пути в сложном пространстве, но линейно увеличивает потребление памяти.ef_construct (по умолчанию 100) — размер динамического списка кандидатов при вставке нового элемента. Чем выше значение, тем дольше Оптимизатор строит сегмент, но тем качественнее (ближе к идеальному) получается структура графа.Разделение хранилища на мелкие линейные сегменты и крупные HNSW-сегменты, использование mmap для обхода ограничений RAM и математическая редукция косинусного расстояния до скалярного произведения делают Qdrant высокопроизводительным движком. Векторная математика работает быстро, когда она изолирована от реляционных блокировок.