Qdrant vector DB concept page. HNSW (Hierarchical Navigable Small World) approximate nearest neighbor index, payload-based filtering with filterable HNSW, distributed cluster with Raft for collections meta, scalar/binary quantization for memory savings. Includes ADR comparing Qdrant vs pgvector vs Pinecone vs Weaviate vs Milvus and scenarios for ingestion (embed → HNSW insert), ANN query with payload filter, vector update with tombstone, quantization trade-offs, and vendor decision tree.
Embedding-модели (text-embedding-3, CLIP, BERT) превращают текст/картинку в вектор float[384..1536], где «семантически близкое» = «близкое по cosine». Хранить эти вектора в Postgres и делать ORDER BY cos_dist LIMIT 10 — это O(N×D): на 10M документах × 1536 измерений = брут-форс на каждый запрос, секунды латенси.
Vector DB — это ANN-индекс (HNSW / IVF / ScaNN) + payload-фильтр + scalar storage, оптимизированный под высокую размерность. Foundation для RAG, semantic search, рекомендаций, image similarity, дедупликации, anomaly detection.
«Vector DB — это approximate nearest neighbor с трейд-оффом recall vs latency vs RAM; точный kNN в high-dim — brute force, поэтому всё про graph traversal (HNSW) или partitioning (IVF) с настраиваемой точностью.»
Три ручки крутятся всегда:
ef_search / nprobe напрямую её регулируют;Payload (метаданные: tenant, year, topic) живёт рядом с вектором, и важно, как фильтр применяется относительно ANN: pre-filter, post-filter или filterable HNSW. Это решает, работает ли «найди ML-статьи за 2024 от tenant X» за 5 мс или сваливается в brute force.
hash(doc_id) % N.Edges: клиент ходит к embedder за вектором, ко всем шардам Qdrant (любой шард может быть coordinator с fan-out на остальные), и в Postgres по итоговым ID. Между нодами Qdrant — Raft для consistency коллекций и репликации шардов.
Документ превращается в вектор и записывается на нужный шард. HNSW-insert — это не «append в B-tree», а встраивание точки в граф: для нового point выбирается случайный уровень L, на каждом уровне ищутся ef_construction ближайших соседей и проставляются edges. Payload (tenant, year, topic) кладётся в RocksDB рядом, шард реплицируется на 2 другие ноды через Raft. Память: 10M × 1536-dim float32 ≈ 60 GB RAM без quantization.
Запрос «ML papers from 2024 about transformers». Embedder делает query vector, coordinator (любая нода Qdrant) делает fan-out на все шарды. На каждом шарде — filterable HNSW: payload-condition проверяется прямо во время graph traversal, а не pre/post-filter. Scatter-gather собирает top-K=10 с каждого шарда, мержит top-30, возвращает top-10 финальных по cosine. Полный текст докачивается из Postgres по ID. Latency ~10 ms p99 на shard в RAM.
Re-embedding после смены модели (или просто правка документа) не мутирует узел графа — HNSW immutable on insert. Старая точка помечается tombstone в bitmap, новая вставляется как обычный insert с новым point_id. Граф загрязняется, RAM/disk растут линейно с upsert-трафиком. Background optimize периодически перестраивает segment без deleted points и делает atomic swap — это IO-heavy, регулируется indexing_threshold. Gotcha: смена embedding model (ada → 3-small) делает все вектора несравнимыми по distance — нужен full re-import dual-write, не upsert.
100M × 1536-dim float32 = 614 GB raw vectors + ~40 GB HNSW graph = ~650 GB RAM. На одной ноде обычно 256-512 GB — не помещаемся, swap = death. Scalar int8 даёт 4× компрессию (614 → 154 GB), recall 99% → 97%. Binary quantization — 32× (614 → 19 GB), recall ~75%, обязательно с rerank: HNSW traversal на binary даёт top-100 кандидатов, потом rescore на оригинальных float32 → top-10. Product Quantization (PQ) — 16-32×, используется в FAISS/Milvus для billion-scale.
Decision tree:
Контекст. Vector store выбирается под четыре оси: (1) шкала векторов, (2) сложность payload-фильтров и hybrid search, (3) operational model, (4) quantization needs.
Шкала. pgvector ломается на ~10M-100M в одной таблице: recall падает, latency растёт, IVFFlat-reindex блокирует writes. Qdrant/Weaviate комфортно держат 100M-1B на single cluster. Milvus и Vespa — billions с GPU FAISS.
Payload-фильтры. Qdrant сделал filterable HNSW first-class — фильтр encoded в graph traversal, не pre/post. pgvector делает фильтр через WHERE поверх ANN-кандидатов: при sparse-фильтре (year=2024 AND tenant=X) top-K часто пуст, и IVFFlat сваливается в brute force. Weaviate тащит хороший hybrid через GraphQL.
Ops. Pinecone полностью managed, нулевая SRE-нагрузка, цена и lock-in. Qdrant/Weaviate/Milvus — self-host либо managed cloud SKU. pgvector живёт внутри существующей Postgres: один store, один backup, transactional consistency с бизнес-данными.
Quantization. Scalar int8 — safe default (4× RAM, 1-3% recall loss). Binary — для CLIP/BERT после rerank (32×, ~75% recall). PQ — для billion-scale.
Решение. pgvector если уже Postgres И <10M И простые фильтры. Pinecone если managed без вопросов. Qdrant — production sweet spot 1M-1B с сложными payload-фильтрами. Weaviate — если приоритет hybrid. Milvus/Vespa — billions + GPU.
Последствия. HNSW не shrinkable (delete = tombstone, periodic optimize обязателен). Backup = десятки GB снепшот index + payload. Embedding model upgrade = full re-import dual-write. Multi-tenancy — обычно лучше отдельные коллекции, чем filter by tenant_id.
embedding(query) хотя бы в Memcached/Redis — это $0.02/1M токенов × QPS = заметные деньги.UPDATE vec = ...). HNSW не мутируется — это tombstone + insert + загрязнение графа. Bulk re-embedding делайте dual-write в новую коллекцию + cutover.WHERE email = ..., WHERE order_id = ...) — Postgres B-tree быстрее и проще.embedding(query) и top-K результатов снимает большую часть нагрузки.