# Milvus

> **La base de datos vectorial de código abierto más escalable para aplicaciones de IA — construida para miles de millones de vectores**

Milvus es una base de datos vectorial de código abierto creada específicamente para búsquedas de similitud escalables y aplicaciones de IA. Originalmente creada por Zilliz y donada a la LF AI & Data Foundation, Milvus impulsa cargas de trabajo de IA en producción en empresas como NVIDIA, AT\&T, IBM y Salesforce. Es la opción preferida cuando necesitas escalar a miles de millones de vectores.

**GitHub:** [milvus-io/milvus](https://github.com/milvus-io/milvus) — 32K+ ⭐

***

## Milvus vs Qdrant — Cuándo elegir cuál

| Criterios                    | Milvus                            | Qdrant                |
| ---------------------------- | --------------------------------- | --------------------- |
| Escala                       | Miles de millones de vectores     | Cientos de millones   |
| Arquitectura                 | Distribuido (múltiples servicios) | Binario único         |
| Complejidad de configuración | Más alto                          | Más bajo              |
| Soporte de índice en GPU     | ✅ GPU FAISS nativo                | Limitado              |
| Multitenencia                | ✅ Particiones + alias             | Basado en colecciones |
| Ingesta por streaming        | ✅ Kafka/Pulsar                    | Limitado              |
| Búsqueda híbrida             | ✅ Denso + disperso                | ✅                     |
| Opción gestionada en la nube | Zilliz Cloud                      | Qdrant Cloud          |

{% hint style="success" %}
**Elige Milvus cuando:** Necesites escalar a miles de millones de vectores, requieras indexación acelerada por GPU (IVF\_FLAT\_GPU), o necesites funciones empresariales como multiusuario, ingesta por streaming y control de acceso basado en roles.
{% endhint %}

***

## Arquitectura de Milvus

Milvus en modo standalone (servidor único) incluye:

* **milvus** — el servicio principal (coordinadores de proxy, consulta, datos e índice)
* **etcd** — almacenamiento de metadatos y descubrimiento de servicios
* **MinIO** — almacenamiento de objetos para datos de segmentos

En modo distribuido (clúster), cada componente escala de forma independiente.

***

## Prerrequisitos

* Cuenta en Clore.ai con alquiler de GPU
* Docker Compose (usualmente preinstalado)
* Conocimientos básicos de Python
* 16GB+ de RAM (32GB recomendados para producción)

***

## Paso 1 — Alquila un servidor GPU en Clore.ai

1. Ve a [clore.ai](https://clore.ai) → **Marketplace**
2. **GPU recomendada:** RTX 4090 o A100 para indexación acelerada por GPU
3. **Alternativa CPU:** Cualquier servidor con 32GB+ de RAM para indexación basada en CPU

**Requisitos mínimos:**

* CPU: 8 núcleos
* RAM: 16GB (32GB recomendados)
* Disco: 50GB SSD/NVMe
* GPU: Opcional (requerida solo para tipos de índices en GPU)

{% hint style="info" %}
**Tipos de índices GPU en Milvus** (IVF\_FLAT\_GPU, IVFSQ8\_GPU) requieren GPUs con capacidad CUDA y aceleran drásticamente la construcción de índices para colecciones grandes. Si planeas indexar con frecuencia más de 10M de vectores, la indexación en GPU se amortiza rápidamente.
{% endhint %}

***

## Paso 2 — Desplegar Milvus en modo standalone

**Imagen Docker:**

```
milvusdb/milvus:v2.4.0
```

Milvus standalone requiere etcd y MinIO. Usa Docker Compose para la configuración más sencilla.

**Puertos:**

```
22
19530
```

* **Puerto 19530:** Puerto SDK/gRPC de Milvus (principal)
* **Puerto 9091:** API REST de Milvus y verificación de estado (interno)

**Variables de entorno:**

```
NVIDIA_VISIBLE_DEVICES=all
NVIDIA_DRIVER_CAPABILITIES=compute,utility
```

***

## Paso 3 — Configurar con Docker Compose

Conéctate por SSH a tu servidor Clore.ai y crea el archivo compose:

```bash
ssh root@<ip-del-servidor> -p <puerto-ssh>

# Instalar Docker Compose si no está presente
which docker-compose || pip install docker-compose
# O usar el plugin de Docker:
docker compose version

# Crear directorio del proyecto
mkdir -p /opt/milvus && cd /opt/milvus

# Descargar el archivo compose oficial de Milvus standalone
wget https://github.com/milvus-io/milvus/releases/download/v2.4.0/milvus-standalone-docker-compose.yml \
    -O docker-compose.yml

# Revisar el archivo compose
cat docker-compose.yml
```

### Personalizar docker-compose.yml

```yaml
version: '3.5'

services:
  etcd:
    container_name: milvus-etcd
    image: quay.io/coreos/etcd:v3.5.5
    environment:
      - ETCD_AUTO_COMPACTION_MODE=revision
      - ETCD_AUTO_COMPACTION_RETENTION=1000
      - ETCD_QUOTA_BACKEND_BYTES=4294967296
      - ETCD_SNAPSHOT_COUNT=50000
    volumes:
      - /opt/milvus/etcd:/etcd
    command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
    healthcheck:
      test: ["CMD", "etcdctl", "endpoint", "health"]
      intervalo: 30s
      timeout: 20s
      reintentos: 3

  minio:
    container_name: milvus-minio
    image: minio/minio:RELEASE.2023-03-13T19-46-17Z
    environment:
      MINIO_ACCESS_KEY: minioadmin
      MINIO_SECRET_KEY: minioadmin
    ports:
      - "9001:9001"
      - "9000:9000"
    volumes:
      - /opt/milvus/minio:/minio_data
    command: minio server /minio_data --console-address ":9001"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      intervalo: 30s
      timeout: 20s
      reintentos: 3

  standalone:
    container_name: milvus-standalone
    image: milvusdb/milvus:v2.4.0
    command: ["milvus", "run", "standalone"]
    security_opt:
      - seccomp:unconfined
    environment:
      ETCD_ENDPOINTS: etcd:2379
      MINIO_ADDRESS: minio:9000
    volumes:
      - /opt/milvus/milvus:/var/lib/milvus
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
      intervalo: 30s
      start_period: 90s
      timeout: 20s
      reintentos: 3
    ports:
      - "19530:19530"
      - "9091:9091"
    depends_on:
      - "etcd"
      - "minio"
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu]  # Habilitar acceso a GPU
```

### Iniciar Milvus

```bash
cd /opt/milvus
docker compose up -d

# Esperar a que los servicios arranquen (~60 segundos)
sleep 60

# Comprobar que todos los servicios estén saludables
docker compose ps

# Comprobar la salud de Milvus
curl http://localhost:9091/healthz
# Esperado: {"status":"ok"}

# Ver registros
docker compose logs -f standalone --tail 50
```

***

## Paso 4 — Instalar el cliente de Python

```bash
pip install pymilvus sentence-transformers numpy tqdm

# Verificar conexión
python3 << 'EOF'
from pymilvus import connections, utility

connections.connect("default", host="localhost", port="19530")
print(f"¡Milvus conectado!")
print(f"Versión: {utility.get_server_version()}")
EOF
```

***

## Paso 5 — Crear una colección

En Milvus, una **colección** es similar a una tabla de base de datos. Tiene un esquema con campos tipados, incluidos campos vectoriales.

```python
from pymilvus import (
    connections,
    FieldSchema,
    CollectionSchema,
    DataType,
    Collection,
    utility
)

# Conectar
connections.connect("default", host="localhost", port="19530")

# Definir esquema
fields = [
    FieldSchema(
        name="id",
        dtype=DataType.INT64,
        is_primary=True,
        auto_id=True           # Generar IDs automáticamente
    ),
    FieldSchema(
        name="text",
        dtype=DataType.VARCHAR,
        max_length=2048        # Longitud máxima del texto
    ),
    FieldSchema(
        name="source",
        dtype=DataType.VARCHAR,
        max_length=256
    ),
    FieldSchema(
        name="category",
        dtype=DataType.VARCHAR,
        max_length=128
    ),
    FieldSchema(
        name="year",
        dtype=DataType.INT32
    ),
    FieldSchema(
        name="embedding",
        dtype=DataType.FLOAT_VECTOR,
        dim=384                # Dimensión de tu modelo de embeddings
    )
]

schema = CollectionSchema(
    fields=fields,
    description="Embeddings de documentos para búsqueda semántica",
    enable_dynamic_field=True  # Permitir agregar campos no presentes en el esquema
)

# Crear colección
collection_name = "documents"
if utility.has_collection(collection_name):
    utility.drop_collection(collection_name)

collection = Collection(
    name=collection_name,
    schema=schema,
    using="default"
)
print(f"¡Colección '{collection_name}' creada!")
```

***

## Paso 6 — Crear índice

Antes de cargar datos para búsqueda, crea un índice apropiado:

```python
from pymilvus import Collection

collection = Collection("documents")

# Índice HNSW (mejor para la mayoría de casos, baja latencia)
hnsw_params = {
    "metric_type": "COSINE",     # COSINE, L2 o IP (Producto Interno)
    "index_type": "HNSW",
    "params": {
        "M": 16,                 # Conectividad del grafo HNSW (8-64)
        "efConstruction": 200    # Profundidad de búsqueda en tiempo de construcción
    }
}

# Índice IVF_FLAT (CPU, bueno para colecciones grandes)
ivf_params = {
    "metric_type": "COSINE",
    "index_type": "IVF_FLAT",
    "params": {
        "nlist": 1024            # Número de clústeres (la raíz cuadrada del tamaño de datos es típico)
    }
}

# Índice GPU_IVF_FLAT (requiere GPU CUDA — el más rápido para consultas por lotes)
gpu_ivf_params = {
    "metric_type": "L2",
    "index_type": "GPU_IVF_FLAT",
    "params": {
        "nlist": 1024,
        "cache_dataset_on_device": True
    }
}

# Crear índice en el campo embedding
collection.create_index(
    field_name="embedding",
    index_params=hnsw_params,
    index_name="embedding_idx"
)

# Crear índice escalar para búsquedas filtradas
collection.create_index(field_name="category", index_name="category_idx")
collection.create_index(field_name="year", index_name="year_idx")

print("¡Índices creados!")
collection.load()  # Cargar en memoria para buscar
```

***

## Paso 7 — Insertar datos

```python
from pymilvus import Collection
from sentence_transformers import SentenceTransformer
import tqdm

collection = Collection("documents")
model = SentenceTransformer("all-MiniLM-L6-v2", device="cuda")

# Tus documentos
documents = [
    {
        "text": "Milvus es una base de datos vectorial de código abierto para aplicaciones de IA escalables.",
        "source": "documentation",
        "category": "database",
        "year": 2024
    },
    {
        "text": "HNSW proporciona búsqueda aproximada de vecinos más cercanos rápida con alta recuperación.",
        "source": "research",
        "category": "algorithm",
        "year": 2023
    },
    {
        "text": "La indexación acelerada por GPU reduce drásticamente el tiempo de construcción para grandes colecciones vectoriales.",
        "source": "blog",
        "category": "performance",
        "year": 2024
    },
    # Agrega miles de documentos más aquí
]

def insert_batch(docs: list, batch_size: int = 1000):
    texts = [d["text"] for d in docs]
    
    # Embedding acelerado por GPU
    embeddings = model.encode(
        texts,
        batch_size=256,
        show_progress_bar=False,
        normalize_embeddings=True
    )
    
    # Insertar en Milvus
    data = {
        "text": [d["text"] for d in docs],
        "source": [d["source"] for d in docs],
        "category": [d["category"] for d in docs],
        "year": [d["year"] for d in docs],
        "embedding": embeddings.tolist()
    }
    
    result = collection.insert(data)
    return result.insert_count

# Insertar en lotes
BATCH_SIZE = 1000
total_inserted = 0

for i in range(0, len(documents), BATCH_SIZE):
    batch = documents[i:i + BATCH_SIZE]
    count = insert_batch(batch)
    total_inserted += count
    print(f"Inserted {total_inserted}/{len(documents)} documents")

# Forzar flush para asegurar que los datos se persistan e indexen
collection.flush()
print(f"Total inserted and flushed: {total_inserted}")
```

***

## Paso 8 — Buscar y consultar

### Búsqueda semántica básica

```python
from pymilvus import Collection
from sentence_transformers import SentenceTransformer

collection = Collection("documents")
collection.load()

model = SentenceTransformer("all-MiniLM-L6-v2", device="cuda")

def search(query: str, top_k: int = 10):
    query_embedding = model.encode(
        [query],
        normalize_embeddings=True
    )[0].tolist()
    
    results = collection.search(
        data=[query_embedding],
        anns_field="embedding",
        param={
            "metric_type": "COSINE",
            "params": {"ef": 64}    # Parámetro de búsqueda HNSW en tiempo de consulta (ef >= top_k)
        },
        limit=top_k,
        output_fields=["text", "source", "category", "year"]
    )
    
    return results[0]

# Buscar
hits = search("how does vector similarity search work")
for hit in hits:
    print(f"Score: {hit.score:.4f}")
    print(f"Text: {hit.entity.get('text')[:100]}")
    print(f"Source: {hit.entity.get('source')}")
    print()
```

### Búsqueda filtrada

```python
from pymilvus import Collection

collection = Collection("documents")

# Búsqueda con filtro de metadatos (expresión booleana)
results = collection.search(
    data=[query_embedding],
    anns_field="embedding",
    param={"metric_type": "COSINE", "params": {"ef": 64}},
    limit=10,
    expr='category == "database" and year >= 2023',  # Filtro booleano
    output_fields=["text", "category", "year"]
)
```

### Búsqueda híbrida (Denso + Disperso)

```python
# Milvus 2.4+ soporta búsqueda híbrida densa+dispersa
from pymilvus import AnnSearchRequest, WeightedRanker, Collection

collection = Collection("documents")

# Solicitud de búsqueda densa
dense_req = AnnSearchRequest(
    data=[dense_embedding],
    anns_field="embedding",
    param={"metric_type": "COSINE", "params": {"ef": 64}},
    limit=20
)

# Solicitud de búsqueda dispersa (requiere campo de vector disperso)
sparse_req = AnnSearchRequest(
    data=[sparse_embedding],
    anns_field="sparse_embedding",
    param={"metric_type": "IP"},
    limit=20
)

# Combinar con Reciprocal Rank Fusion
results = collection.hybrid_search(
    [dense_req, sparse_req],
    rerank=WeightedRanker(0.7, 0.3),  # 70% denso, 30% disperso
    limit=10,
    output_fields=["text"]
)
```

***

## Paso 9 — Construir un servicio RAG

```bash
pip install fastapi uvicorn openai

cat > /workspace/milvus_rag.py << 'EOF'
from fastapi import FastAPI
from pydantic import BaseModel
from pymilvus import Collection, connections
from sentence_transformers import SentenceTransformer
from openai import OpenAI
import os

app = FastAPI(title="Milvus RAG API")

# Inicializar al inicio
connections.connect("default", host="localhost", port="19530")
collection = Collection("documents")
collection.load()
embedder = SentenceTransformer("all-MiniLM-L6-v2", device="cuda")
llm = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

class QueryRequest(BaseModel):
    question: str
    n_results: int = 5

@app.get("/health")
async def health():
    return {"status": "ok", "vectors": collection.num_entities}

@app.post("/search")
async def semantic_search(req: QueryRequest):
    embedding = embedder.encode(
        [req.question],
        normalize_embeddings=True
    )[0].tolist()
    
    results = collection.search(
        data=[embedding],
        anns_field="embedding",
        param={"metric_type": "COSINE", "params": {"ef": 64}},
        limit=req.n_results,
        output_fields=["text", "source", "category"]
    )
    
    return {
        "results": [
            {
                "text": hit.entity.get("text"),
                "source": hit.entity.get("source"),
                "score": hit.score
            }
            for hit in results[0]
        ]
    }

@app.post("/rag")
async def rag(req: QueryRequest):
    embedding = embedder.encode([req.question], normalize_embeddings=True)[0].tolist()
    
    hits = collection.search(
        data=[embedding],
        anns_field="embedding",
        param={"metric_type": "COSINE", "params": {"ef": 64}},
        limit=req.n_results,
        output_fields=["text", "source"]
    )[0]
    
    context = "\n\n".join([
        f"[{hit.entity.get('source')}]: {hit.entity.get('text')}"
        for hit in hits if hit.score > 0.4
    ])
    
    response = llm.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "Responde basándote en el contexto. Sé conciso."},
            {"role": "user", "content": f"Context:\n{context}\n\nQuestion: {req.question}"}
        ]
    )
    
    return {"answer": response.choices[0].message.content, "context_used": len(hits)}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)
EOF

python3 /workspace/milvus_rag.py
```

***

## Paso 10 — Monitorizar y gestionar

```python
from pymilvus import connections, utility, Collection

connections.connect("default", host="localhost", port="19530")

# Listar todas las colecciones
print("Colecciones:", utility.list_collections())

# Estadísticas de la colección
col = Collection("documents")
print(f"Conteo de entidades: {col.num_entities:,}")
print(f"Esquema: {col.schema}")

# Gestión de particiones
col.create_partition("2024_docs")
col.create_partition("2023_docs")

# Insertar con partición
col.insert(data, partition_name="2024_docs")

# Buscar en partición específica
results = col.search(
    data=[query_vec],
    anns_field="embedding",
    param={"metric_type": "COSINE", "params": {"ef": 64}},
    limit=10,
    partition_names=["2024_docs"]  # Buscar solo en esta partición
)
```

***

## Solución de problemas

### Servicios que no arrancan

```bash
# Comprobar logs de contenedores
docker compose logs etcd
docker compose logs minio
docker compose logs standalone

# Comprobar espacio en disco
df -h /opt/milvus

# Reiniciar servicios
docker compose restart
```

### Conexión rechazada en 19530

```bash
# Verificar que Milvus esté escuchando
netstat -tlnp | grep 19530

# Comprobar estado de salud
curl http://localhost:9091/healthz

# Dar tiempo para el arranque (90 segundos)
docker compose logs standalone | tail -20
```

### Tiempo de espera en la construcción de índices para colecciones grandes

```python
# Aumentar el timeout para construcciones de índices grandes
from pymilvus import Collection

collection = Collection("documents")
collection.create_index(
    field_name="embedding",
    index_params=hnsw_params,
    timeout=3600  # timeout de 1 hora
)
```

### Alto uso de memoria

```bash
# Configurar límites de memoria de Milvus en docker-compose.yml
# Añadir al servicio standalone:
deploy:
  resources:
    limits:
      memory: 16g
```

***

## Guía de selección de tipo de índice

| Tipo de índice | Mejor para                     | Memoria      | Velocidad | GPU requerida |
| -------------- | ------------------------------ | ------------ | --------- | ------------- |
| FLAT           | Pequeño (<1M), búsqueda exacta | Alta         | Lento     | No            |
| IVF\_FLAT      | Medio (1M–10M)                 | Medio        | Bueno     | No            |
| HNSW           | Baja latencia, <100M           | Alta         | Excelente | No            |
| IVF\_SQ8       | Comprimido, grande             | Bajo         | Bueno     | No            |
| GPU\_IVF\_FLAT | Consultas por lotes rápidas    | GPU+RAM      | Mejor     | Sí            |
| DISKANN        | Escala de miles de millones    | Bajo (disco) | Bueno     | No            |

***

## Benchmarks de rendimiento

| Tamaño de colección | Índice         | GPU      | QPS      |
| ------------------- | -------------- | -------- | -------- |
| 1M de vectores      | HNSW           | RTX 3090 | \~8,000  |
| 10M de vectores     | IVF\_FLAT      | RTX 4090 | \~2,500  |
| 10M de vectores     | GPU\_IVF\_FLAT | A100     | \~12,000 |
| 100M de vectores    | DISKANN        | A100     | \~1,200  |

***

## Recursos adicionales

* [Documentación de Milvus](https://milvus.io/docs)
* [GitHub de Milvus](https://github.com/milvus-io/milvus)
* [Documentación de PyMilvus](https://milvus.io/api-reference/pymilvus/v2.4.x/About.md)
* [Bootcamp de Milvus](https://github.com/milvus-io/bootcamp) — Aplicaciones de ejemplo
* [Zilliz Cloud](https://cloud.zilliz.com/) — Milvus gestionado
* [Comparación de bases de datos vectoriales](https://milvus.io/docs/benchmark.md)
* [Attu GUI](https://github.com/zilliztech/attu) — Interfaz web para la gestión de Milvus

***

*Milvus en Clore.ai es la solución ideal para aplicaciones de IA que necesitan escalar más allá de cientos de millones de vectores. Combinado con generación de embeddings acelerada por GPU, puedes construir sistemas de búsqueda semántica y RAG de clase mundial a una fracción del coste de nubes gestionadas.*

***

## Recomendaciones de GPU en Clore.ai

| Caso de uso                      | GPU recomendada | Coste estimado en Clore.ai |
| -------------------------------- | --------------- | -------------------------- |
| Desarrollo/Pruebas               | RTX 3090 (24GB) | \~$0.12/gpu/hr             |
| Búsqueda vectorial en producción | RTX 3090 (24GB) | \~$0.12/gpu/hr             |
| Embeddings de alto rendimiento   | RTX 4090 (24GB) | \~$0.70/gpu/hr             |

> 💡 Todos los ejemplos en esta guía pueden desplegarse en [Clore.ai](https://clore.ai/marketplace) servidores GPU. Navega las GPUs disponibles y alquila por hora — sin compromisos, acceso root completo.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.clore.ai/guides/guides_v2-es/rag-y-bases-de-datos-vectoriales/milvus.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
