Qdrant - Vector Database
Оптимизация производительности
Два проекта, одинаковые коллекции, одинаковые серверы. Первый: загрузка 1M векторов - 2 часа, поиск - 150ms P99. Второй: загрузка - 8 минут, поиск - 12ms P99. Разница только в настройках: indexing_threshold, batch size, ef, memmap. Performance tuning - это не трюк, это понимание как работает HNSW внутри.
- **Ночная переиндексация:** отключить HNSW (indexing_threshold: 0) на время загрузки → 5x ускорение. 10M документов за 20 минут вместо 2 часов
- **RAG с высоким recall:** ef=256 + oversampling=4 для финальных запросов, ef=64 для кандидатов - точность 99.9% при приемлемой latency
- **Экономия ресурсов:** Binary Quantization + on_disk float32 - 50M векторов на сервере с 16 GB RAM вместо требуемых 300 GB
Предварительные знания
indexing_threshold и Batch Upsert: скорость загрузки данных
**Производительность при загрузке данных** зависит от двух факторов: как вы пишете точки (batch vs одиночные вставки) и когда Qdrant строит HNSW-индекс (`indexing_threshold`). Неправильные настройки могут делать загрузку в 10-100 раз медленнее.
| indexing_threshold | Поиск до достижения | Поиск после | Когда использовать |
|---|---|---|---|
| 0 | Flat (точный, медленный) | HNSW строится сразу | Маленькие коллекции (< 10k) |
| 10000 | Flat до 10k точек | HNSW > 10k | Баланс для частых small writes |
| 20000 (default) | Flat до 20k точек | HNSW > 20k | Стандарт для большинства задач |
| 0 (во время загрузки) | Flat (медленно) | HNSW после включения | Initial bulk load - максимальная скорость записи |
Вы загружаете 10M векторов в новую коллекцию. Batch size = 100, wait = false, indexing_threshold = 20000 (default). Загрузка идёт медленнее чем ожидалось. Что поможет?
Memmap: хранение на диске с доступом через память
**Memmap (memory-mapped files)** позволяет Qdrant хранить векторы на SSD вместо RAM, обращаясь к ним через виртуальную память. OS кэширует «горячие» страницы. Результат: коллекции больше RAM, небольшая потеря latency только при cache miss.
**Тип диска критически важен для memmap.** NVMe SSD: latency ~0.1ms, отличная производительность. SATA SSD: latency ~0.5ms, приемлемо. HDD: latency ~5-10ms, неприемлемо для поиска. Если Qdrant на HDD - уберите memmap или замените диск.
Коллекция: 20M векторов, memmap включён. Запросы к «популярным» документам быстрые (2ms), к «редким» - медленные (50ms). Почему?
ef и hnsw_ef: баланс скорость vs качество поиска
**HNSW имеет два ключевых параметра** влияющих на качество и скорость поиска. `ef` (search ef) - размер очереди кандидатов при поиске. `hnsw_ef` в конфиге коллекции - дефолтный ef. Выше ef = лучший recall, больше latency.
**Быстрый рецепт production тюнинга:** 1) Binary Quantization + `always_ram: true` - основная экономия RAM. 2) `on_disk: true` для float32 - оригинальные векторы на NVMe. 3) `indexing_threshold: 0` при bulk load → `20000` после. 4) `hnsw_ef: 128` по умолчанию, настраивайте выше для критичных запросов. 5) Batch upsert 100-200, `wait: false`. Это покрывает 90% production use cases.
«Чем больше параметр m в HNSW, тем лучше - нужно поставить m=64 для максимального recall»
m=16 даёт recall 99%+ в большинстве задач. Увеличение m до 32-64 незначительно улучшает recall но удваивает-утетверяет RAM для HNSW графа и замедляет построение. Первый рычаг улучшения recall - ef_construct и ef при поиске, а не m.
При m=16 каждый узел имеет 16 соседей в нижних слоях. Recall при ef=128 ≈ 98-99%. m=32 даёт 99%+ но граф в 2x тяжелее. Правило: m=16 default, m=32 только если Scalar/Binary quantization с ef=256 всё ещё не даёт нужный recall.
Поиск возвращает 10 результатов за 8ms (p50), но recall@10 = 92% (нужно 99%+). Коллекция: 5M векторов, Binary Quantization, ef=64. Что изменить?
Ключевые идеи
- **Batch upsert** (100-200 точек, wait: false) в 50-100x быстрее одиночных вставок
- **indexing_threshold: 0** при bulk load - отключает HNSW rebuild. После загрузки верните 20000
- **Memmap** (on_disk: true) - векторы на NVMe SSD, OS кэширует горячие страницы. Требует NVMe, не HDD
- **ef при поиске** - главный рычаг recall vs latency: ef=64 (быстро), ef=128 (default), ef=256 (точно)
- **Binary Quantization + on_disk float32** - стандартный паттерн для коллекций больше доступного RAM
Что дальше
Вы прошли весь Production раздел. Теперь знаете как деплоить, масштабировать, мониторить и оптимизировать Qdrant.
- Квантизация — Binary и Scalar Quantization - фундамент performance оптимизации
- Мониторинг — Метрики pending_optimizations и latency - входные данные для тюнинга
- HNSW: Как работает индекс — Понимание HNSW изнутри объясняет почему ef и m работают именно так
Вопросы для размышления
- Почему indexing_threshold: 0 ускоряет bulk load, но замедляет поиск во время загрузки? Опишите что происходит с данными при indexing_threshold = 0 vs 20000.
- У вас коллекция с m=16, ef_construct=100. Вы измерили recall@10 = 95%, хотите 99%. Какой параметр изменить и почему - m, ef_construct, или ef при поиске? Какой из них требует пересоздания коллекции?
- Спроектируйте оптимальную конфигурацию Qdrant для: 100M векторов 1536-dim, 64 GB RAM, NVMe SSD 2 TB, требуемый recall 99%, P99 latency < 50ms. Опишите все настройки и обоснование.