Módulo 2: Cómo funcionan Vector Databases (Conceptual)
Cápsula 02: Arquitectura de Vector Databases (3 Capas)
🎯 Objetivo de la cápsula
Entender las 3 capas arquitectónicas de una vector database (Indexing, Query Engine, Storage) y cómo interactúan para lograr búsqueda eficiente en millones de vectores.
Al finalizar esta cápsula:
- ✅ Explicarás las 3 capas y su responsabilidad
- ✅ Entenderás el flujo completo: Insert → Index → Query → Retrieve
- ✅ Comprenderás por qué separación en capas es importante para performance
Tiempo estimado: 10-12 minutos
🏗️ Las 3 capas de Vector Database
Una vector database NO es solo "almacenar embeddings en disco". Es una arquitectura de 3 capas diseñada para búsqueda eficiente a escala.
Visión general
┌─────────────────────────────────────────┐
│ LAYER 1: INDEXING │
│ Construir estructura para búsqueda │
│ rápida (HNSW, IVF, PQ) │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ LAYER 2: QUERY ENGINE │
│ Ejecutar búsqueda en índice │
│ (ANN search, filtering, ranking) │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ LAYER 3: STORAGE │
│ Persistir vectores + metadata en disco │
│ (durability, backups, replication) │
└─────────────────────────────────────────┘
Clave: Cada capa tiene responsabilidad específica. Esto permite optimizar cada una independientemente.
Layer 1: Indexing (Construcción de índice)
¿Qué hace?
Construye estructura de datos especializada para búsqueda rápida, transformando colección desordenada de vectores en grafo/árbol navegable.
¿Por qué es importante?
Sin indexing, búsqueda es brute force O(n):
# Sin índice (brute force)
for vector in database: # ❌ 1M iteraciones
similarity = cosine(query, vector)
Con indexing, búsqueda es O(log n) o mejor:
# Con índice HNSW (conceptual)
path = navigate_graph(query) # ✅ ~100 saltos (no 1M)
candidates = get_neighbors(path)
Algoritmos principales
| Algoritmo | Tipo | Complejidad | Usado por |
|---|---|---|---|
| HNSW | Grafo navegable jerárquico | O(log n) | ChromaDB, Weaviate, Qdrant |
| IVF | Clustering + buckets | O(sqrt(n)) | Faiss, Milvus |
| PQ | Compresión de vectores | O(n) compressed | Faiss, Milvus (addon) |
| ScaNN | Learned quantization | O(log n) | Google Research |
Profundizaremos en cada algoritmo en Cápsulas 04-06.
Trade-offs
Build time vs Query time:
- HNSW: Build lento (1M vectores = 5-10 min), Query rápido (15ms)
- IVF: Build rápido (1M vectores = 2-3 min), Query medio (30ms)
Memory vs Accuracy:
- HNSW: Mayor memoria (4-8 GB para 1M vectores 1536-dim), accuracy 98%
- IVF+PQ: Menor memoria (1-2 GB), accuracy 88-93%
Layer 2: Query Engine (Motor de búsqueda)
¿Qué hace?
Ejecuta búsqueda en índice usando algoritmo apropiado + aplica filtros + ranking.
Responsabilidades
-
ANN Search (Approximate Nearest Neighbors)
- Navegar índice HNSW/IVF
- Encontrar top-k candidates
-
Metadata filtering (crucial para RAG)
- Filtrar por fecha, autor, categoría
- Ejemplo RAG: Solo buscar en documentos de "customer support"
-
Post-filtering ranking
- Re-score resultados con metadata
- Remover duplicados
Flujo de query
User query → Embed → ANN Search → Filter → Rank → Top-k results
Ejemplo RAG concreto:
# Query
query = "How do I reset my password?"
query_embedding = embed(query) # [1536 dims]
# Layer 2: Query Engine ejecuta
results = db.query(
query_embedding=query_embedding,
k=10, # Top 10 candidates
where={"category": "support", "language": "en"}, # Filtro metadata
)
# Resultados: Top 10 chunks relevantes filtrados
Trade-offs
Filtering antes vs después de ANN:
Pre-filtering (filtrar antes de búsqueda):
- ✅ Más rápido (busca en subset)
- ❌ Puede fallar si subset vacío
- Usado por: Pinecone, Weaviate
Post-filtering (filtrar después de búsqueda):
- ✅ Siempre devuelve resultados
- ❌ Más lento (busca en todo, luego filtra)
- Usado por: ChromaDB (default)
Decisión: Depende de tu caso de uso RAG (volumen de filtros, cardinalidad de metadata).
Layer 3: Storage (Persistencia)
¿Qué hace?
Persiste vectores + metadata en disco con garantías de durabilidad, consistencia, y backups.
Responsabilidades
-
Durability (no perder datos)
- Write-ahead log (WAL)
- Snapshots periódicos
-
Replication (alta disponibilidad)
- Master-replica setup
- Eventual consistency
-
Compression (reducir storage cost)
- Comprimir vectores en disco
- Descomprimir en memoria para query
Implementaciones comunes
| Storage Backend | Usado por | Características |
|---|---|---|
| Embedded DB (SQLite, RocksDB) | ChromaDB | Local, single-node, fácil setup |
| Object Storage (S3, GCS) | Pinecone | Cloud-native, infinitely scalable |
| Columnar Storage (Parquet) | Weaviate | Eficiente para analytics |
| Distributed FS (HDFS, Ceph) | Milvus | Enterprise, multi-node |
Trade-offs
In-memory vs Disk:
In-memory:
- ✅ Latencia ultra-baja (<5ms)
- ❌ Costoso (RAM > disk)
- ❌ Limite de scale (RAM finita)
- Usado por: Redis + RediSearch
Disk-based:
- ✅ Económico (disk es barato)
- ✅ Infinitely scalable
- ❌ Latencia mayor (10-50ms)
- Usado por: ChromaDB, Pinecone, Milvus
Hybrid (mayoría de prod):
- Hot data (reciente) → RAM
- Cold data (viejo) → Disk
- Best of both worlds
🔄 Flujo completo: Insert → Query
Ahora que conoces las 3 capas, veamos cómo interactúan en operaciones reales.
Operación 1: Insert (agregar vectores)
User Layer 1 Layer 2 Layer 3
│ Indexing Query Eng Storage
│
├─ insert(vec) ──→ Build index ──→ (no-op) ──────→ Persist to disk
│ (HNSW add) (append WAL)
│ │ │
│ └─────── Index built ────────────┘
│ (async)
│
└─ ACK inserted
Pasos:
- User inserta vector + metadata
- Layer 1 (Indexing): Agrega vector a índice HNSW (update grafo)
- Layer 3 (Storage): Persiste a disco (write-ahead log)
- ACK al user (operación completa)
Nota: Build index puede ser async (batch updates cada N inserts).
Operación 2: Query (buscar similares)
User Layer 1 Layer 2 Layer 3
│ Indexing Query Eng Storage
│
├─ query(vec) ────→ ANN search ──→ Apply filters ─→ Fetch metadata
│ (navigate (where clause) (from disk)
│ HNSW graph) │ │
│ │ │ │
│ └───── Top-k candidates ────────┘
│ │
│ Re-rank + limit
│ │
└─ ← Results ────────────────────────────┘
Pasos:
- User envía query vector
- Layer 2 (Query Engine): Ejecuta ANN search en índice (Layer 1)
- Layer 1 (Indexing): Navega grafo HNSW → devuelve candidates
- Layer 2 (Query Engine): Aplica filtros metadata
- Layer 3 (Storage): Fetch full metadata de candidates
- Layer 2 (Query Engine): Re-rank + limit a top-k
- Devuelve resultados a user
Latencia típica: 15-50ms (1M vectores, 1536-dim, HNSW)
🏭 Ejemplo real: ChromaDB arquitectura
Veamos cómo ChromaDB implementa estas 3 capas.
Layer 1: Indexing
- Algoritmo: HNSW (default)
- Librería:
hnswlib(C++ binding) - Build strategy: Incremental (agregar vectores uno a uno)
Layer 2: Query Engine
- ANN search: HNSW navigation
- Filtering: Post-filtering (busca primero, filtra después)
- Ranking: Cosine similarity (default) o dot product
Layer 3: Storage
- Backend: DuckDB (embedded columnar DB)
- Durability: Write-ahead log (WAL)
- Compression: Parquet format (vectores + metadata)
Ventaja: Toda la arquitectura en un proceso (no requiere servidor externo).
Desventaja: Single-node (no distributed). Para scale → migrar a Pinecone/Weaviate.
📊 Benchmark: Impact de cada capa
Midamos latencia de cada capa en query típico (1M vectores, 1536-dim, ChromaDB).
| Capa | Operación | Latencia | % del total |
|---|---|---|---|
| Layer 1 | ANN search (HNSW navigate) | 12 ms | 60% |
| Layer 2 | Metadata filtering | 3 ms | 15% |
| Layer 3 | Fetch full metadata from disk | 5 ms | 25% |
| Total | End-to-end query | 20 ms | 100% |
Insights:
- Layer 1 (indexing) domina latencia → Optimizar algoritmo HNSW es crítico
- Layer 3 (storage) es 25% → Usar SSD (no HDD) ayuda significativamente
- Layer 2 (filtering) es barato → Puedes filtrar agresivamente sin penalty
Comparación con brute force:
| Método | Latencia | Speedup |
|---|---|---|
| Brute force (numpy) | 1500 ms | 1x |
| Vector DB (HNSW) | 20 ms | 75x faster |
🤔 Por qué separación en capas importa
Ventaja 1: Optimización independiente
Sin separación:
# ❌ Todo en una función (monolítico)
def search(query):
results = brute_force_search(query) # Slow
filtered = apply_filters(results)
return persist(filtered)
Con separación:
# ✅ Cada capa optimizable independientemente
def search(query):
candidates = layer1.ann_search(query) # Optimizado: HNSW
filtered = layer2.filter(candidates) # Optimizado: Bitmap index
full = layer3.fetch(filtered) # Optimizado: Columnar storage
return full
Resultado: Puedes cambiar algoritmo de Layer 1 (HNSW → IVF) sin tocar Layers 2-3.
Ventaja 2: Scaling horizontal
Layer 1 (Indexing):
- Escala con RAM (índice en memoria)
- Requiere beefy machine (32-64 GB RAM)
Layer 3 (Storage):
- Escala con disk (S3, GCS)
- Puede ser separado a storage cluster
Arquitectura distributed:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Query Node │───→│ Index Node │───→│ Storage Node │
│ (Layer 2) │ │ (Layer 1) │ │ (Layer 3) │
└──────────────┘ └──────────────┘ └──────────────┘
Ejemplo: Pinecone usa arquitectura distribuida (Layers 1-3 en nodos separados).
Ventaja 3: Flexibilidad de configuración
Puedes ajustar cada capa según requisitos:
Caso A: Latencia crítica (chatbot)
- Layer 1: HNSW (alta accuracy, 15ms)
- Layer 2: Pre-filtering (reduce search space)
- Layer 3: In-memory (Redis, <5ms)
- Resultado: <20ms end-to-end
Caso B: Cost-optimized (batch processing)
- Layer 1: IVF (menor memoria, 50ms)
- Layer 2: Post-filtering (simple)
- Layer 3: Disk (S3, 100ms)
- Resultado: <200ms end-to-end (pero 10x más barato)
✅ Checklist de comprensión
Verifica que entendiste esta cápsula:
-
¿Cuáles son las 3 capas de vector database?
- Respuesta: Indexing (construir índice), Query Engine (ejecutar búsqueda), Storage (persistir datos)
-
¿Qué hace Layer 1 (Indexing)?
- Respuesta: Construye estructura de datos especializada (HNSW, IVF, PQ) para búsqueda O(log n) vs O(n)
-
¿Qué hace Layer 2 (Query Engine)?
- Respuesta: Ejecuta ANN search, aplica filtros metadata, re-rank resultados
-
¿Qué hace Layer 3 (Storage)?
- Respuesta: Persiste vectores + metadata con durabilidad, replication, compression
-
¿Por qué separación en capas es importante?
- Respuesta: Permite optimizar cada capa independientemente, escalar horizontalmente, ajustar trade-offs según caso de uso
Si respondiste 4-5/5 correctamente → ✅ Listo para Cápsula 03 (Indexing Algorithms Overview)
🔗 Conexión con RAG
¿Cómo esto ayuda en RAG?
Cuando construyes RAG system, necesitas entender arquitectura para:
Debuggear performance
Problema: "Mi RAG tarda 500ms en retrieval. ¿Dónde está el bottleneck?"
Solución con conocimiento de capas:
- Profile cada capa:
- Layer 1 (indexing): 450ms → Problema aquí (índice mal configurado)
- Layer 2 (query): 30ms
- Layer 3 (storage): 20ms
- Fix: Rebuild índice HNSW con mejores parámetros (efConstruction=200)
Optimizar costos
Problema: "ChromaDB local consume 64 GB RAM. Muy caro."
Solución con conocimiento de capas:
- Layer 1: Cambiar HNSW → IVF (menor memoria)
- Layer 3: Usar disk storage en lugar de in-memory
- Resultado: 16 GB RAM (4x reducción)
Tomar mejores decisiones
Problema: "¿Migrar de ChromaDB a Pinecone?"
Comparación con conocimiento de arquitectura:
| Layer | ChromaDB | Pinecone |
|---|---|---|
| Layer 1 | HNSW (alta accuracy) | Custom (optimizado) |
| Layer 2 | Post-filtering | Pre-filtering |
| Layer 3 | DuckDB (local) | S3 (cloud) |
| Scale | Single-node | Multi-node |
| Latencia | 20ms (local) | 50ms (network) |
| Cost | Free (self-hosted) | $70/month |
Decisión informada: ChromaDB para MVP (<100K vectores), Pinecone para production (>1M vectores).
🚀 Siguiente paso
Ahora que entiendes arquitectura de 3 capas, profundizaremos en Layer 1 (Indexing).
Próxima cápsula: 03 - Indexing Algorithms Overview
Aprenderás:
- Brute force O(n) vs indexing O(log n) (matemáticas simples)
- Principales algoritmos: HNSW, IVF, PQ, ScaNN
- Trade-offs: Accuracy vs Speed vs Memory
- Cuándo usar cada uno
¿Por qué importante? Layer 1 domina 60-70% de latencia de query. Optimizar indexing = mayor impacto en performance.
Tiempo de lectura: 10-12 minutos
Siguiente: 03-indexing-algorithms-overview.md