Qdrant - Vector Database
HNSW: Как работает индекс
Поиск по 1 млн векторов за 1-5ms - это не магия. Это HNSW: граф, по которому можно добраться до любого вектора за O(log N) шагов. Понять его параметры - значит уметь настраивать recall и скорость под конкретную задачу.
- **RAG с высоким recall:** ef_construct=200, hnsw_ef=128 - не пропустим релевантный контекст для LLM
- **Рекомендации в реальном времени:** m=16, hnsw_ef=32 - 1-2ms на запрос, recall 94% достаточно
- **Медицинские данные:** m=32, exact: true для финальной верификации - точность важнее скорости
Предварительные знания
HNSW: Navigable Small World
**HNSW (Hierarchical Navigable Small World)** - алгоритм приближённого поиска ближайших соседей (ANN). Это многослойный граф: верхние слои - «скоростные шоссе» с малым числом узлов и длинными связями, нижний слой - полный граф со всеми векторами и короткими связями.
**«Маленький мир»** - концепция из социологии: любые два человека связаны через ~6 рукопожатий. HNSW использует тот же принцип - из любой точки можно быстро достичь любой другой, двигаясь по «длинным» связям верхних слоёв и уточняя через «короткие» связи нижних.
**HNSW - это ANN, не точный поиск.** Он находит приближённых ближайших соседей, а не гарантированно точных. Recall (доля правильных результатов) обычно 95-99%, что достаточно для большинства задач. Для 100% recall - `exact: true`, но это brute-force.
Поиск с HNSW даёт 9 из 10 правильных результатов (recall = 90%). Коллега предлагает использовать `exact: true` для 100% recall в production. Что правильнее?
Параметры m, ef_construct, ef
**Три ключевых параметра HNSW** определяют качество и скорость работы индекса. Важно понимать каждый из них.
| Параметр | Где задаётся | Default | Что контролирует |
|---|---|---|---|
| m | hnsw_config при создании коллекции | 16 | Количество связей каждого узла в графе. Больше = лучше recall, больше памяти |
| ef_construct | hnsw_config при создании коллекции | 100 | Размер очереди при построении индекса. Больше = лучше граф, дольше индексирование |
| ef (hnsw_ef) | params при поиске | зависит от ef_construct | Размер очереди при поиске. Больше = лучше recall, медленнее запрос |
**ef_construct изменить нельзя после создания коллекции** - нужно пересоздавать. Параметр `m` тоже фиксирован. **hnsw_ef при поиске** - можно менять для каждого запроса. Именно поэтому начинайте с умеренных m/ef_construct, а тюнинг качества делайте через hnsw_ef.
Коллекция создана с m=16, ef_construct=100. Recall при поиске - 94%, цель - 98%. Какой параметр менять?
Тюнинг: память, скорость, качество
**Главный trade-off HNSW:** качество recall ↔ скорость поиска ↔ потребление памяти. Три вершины треугольника - улучшение одного обычно ухудшает другое.
| Сценарий | m | ef_construct | hnsw_ef | Характеристики |
|---|---|---|---|---|
| Быстрый поиск (latency-first) | 8 | 50 | 32 | Минимальная память, максимальная скорость, recall ~90% |
| Баланс (default) | 16 | 100 | 64 | Хороший recall ~97%, умеренная скорость и память |
| Высокий recall | 32 | 200 | 128 | Recall ~99%, +2x память, умеренно медленнее |
| Максимальное качество | 64 | 400 | 256 | Recall ~99.9%, дорого по памяти и скорости |
**Практический совет:** начните с дефолтных m=16, ef_construct=100. Измерьте recall на реальных данных через сравнение с `exact: true`. Если recall < 95% - сначала увеличьте hnsw_ef при поиске. Если даже hnsw_ef=512 не помогает - пересоздайте с m=32, ef_construct=200.
Установить максимальные m=64, ef_construct=400 «на всякий случай»
Начинать с дефолтных значений (m=16, ef_construct=100) и увеличивать только если измеренный recall недостаточен
m=64 vs m=16: память под граф растёт в 4 раза. ef_construct=400 vs 100: индексирование замедляется в ~4 раза. При этом recall на типичных задачах с дефолтами уже 95-97%. Избыточные параметры - пустая трата ресурсов
Коллекция из 5 млн векторов (1536-dim). Измеренный recall@10 = 96% при hnsw_ef=64. Продукт требует 99%+. Что оптимально?
Ключевые идеи
- **HNSW** - многослойный граф, поиск за O(log N). Верхние слои - быстрое приближение, нижний - точное уточнение
- **m** (default: 16) - связность узлов в графе. Больше = лучше recall, больше памяти. Фиксируется при создании
- **ef_construct** (default: 100) - качество построения графа. Больше = лучше граф, дольше индексирование. Фиксируется при создании
- **hnsw_ef** при поиске - можно менять для каждого запроса. Это главный рычаг тюнинга recall без пересоздания коллекции
- **Тюнинг:** сначала hnsw_ef ↑, потом m и ef_construct ↑. Измеряйте recall через сравнение с exact: true
Что дальше
HNSW управляет поиском по векторам. Но часто нужно ещё и фильтровать по полям данных - категории, дате, геолокации.
- Payload индексы — Ускорение фильтрации по payload-полям - работает вместе с HNSW
- Квантизация векторов — Сжатие памяти в 4-32x при минимальной потере recall
- Метрики расстояния — Как выбрать правильную метрику для своей задачи
Вопросы для размышления
- При каких задачах recall 95% достаточен, а при каких нужен 99%+? Приведите примеры из реальных систем.
- Почему ef_construct нельзя изменить после создания коллекции? Как это связано с принципом работы HNSW?
- При ограниченной RAM - какой параметр уменьшить первым: m или ef_construct?