# Milvus

> **Наиболее масштабируемая открытая векторная база данных для AI-приложений — создана для миллиардов векторов**

Milvus — это открытая векторная база данных, специально разработанная для масштабируемого поиска по сходству и AI-приложений. Изначально созданная Zilliz и переданная в LF AI & Data Foundation, Milvus используется в производственных AI-нагрузках в таких компаниях, как NVIDIA, AT\&T, IBM и Salesforce. Это предпочтительный выбор, когда требуется масштабирование до миллиардов векторов.

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

***

## Milvus vs Qdrant — когда выбирать какую

| Критерии                                       | Milvus                                   | Qdrant              |
| ---------------------------------------------- | ---------------------------------------- | ------------------- |
| Масштаб                                        | Миллиарды векторов                       | Сотни миллионов     |
| Архитектура                                    | Распределённый (несколько сервисов)      | Один бинарный файл  |
| Сложность настройки                            | Выше                                     | Ниже                |
| Поддержка GPU-индексов                         | ✅ Встроенный GPU FAISS                   | Ограничено          |
| Многопользовательская изоляция (Multi-tenancy) | ✅ Разделы (partitions) + псевдонимы      | На основе коллекций |
| Потоковый приём данных                         | ✅ Kafka/Pulsar                           | Ограничено          |
| Гибридный поиск                                | ✅ Плотные + разреженные (dense + sparse) | ✅                   |
| Вариант с управляемым облаком                  | Zilliz Cloud                             | Qdrant Cloud        |

{% hint style="success" %}
**Выберите Milvus, когда:** Вам нужно масштабироваться до миллиардов векторов, требуется индексирование с ускорением на GPU (IVF\_FLAT\_GPU) или нужны корпоративные функции, такие как мультиарендность, потоковый приём данных и управление доступом на основе ролей.
{% endhint %}

***

## Архитектура Milvus

Milvus в автономном режиме (один сервер) включает:

* **milvus** — основной сервис (координаторы proxy, query, data, index)
* **etcd** — хранилище метаданных и обнаружение сервисов
* **MinIO** — объектное хранилище для данных сегментов

В распределённом режиме (кластер) каждый компонент масштабируется независимо.

***

## Требования

* Учетная запись Clore.ai с арендой GPU
* Docker Compose (обычно предустановлен)
* Базовые знания Python
* 16ГБ+ ОЗУ (рекомендуется 32ГБ для продакшена)

***

## Шаг 1 — Арендуйте сервер с GPU на Clore.ai

1. Перейдите на [clore.ai](https://clore.ai) → **Маркетплейс**
2. **Рекомендуемый GPU:** RTX 4090 или A100 для индексирования с ускорением на GPU
3. **Альтернатива на CPU:** Любой сервер с 32ГБ+ ОЗУ для индексирования на CPU

**Минимальные требования:**

* CPU: 8 ядер
* ОЗУ: 16ГБ (рекомендуется 32ГБ)
* Диск: 50ГБ SSD/NVMe
* GPU: опционально (требуется только для типов индексов на GPU)

{% hint style="info" %}
**Типы GPU-индексов в Milvus** (IVF\_FLAT\_GPU, IVFSQ8\_GPU) требуют GPU с поддержкой CUDA и существенно ускоряют построение индексов для больших коллекций. Если вы планируете часто индексировать более 10M векторов, индексирование на GPU быстро окупается.
{% endhint %}

***

## Шаг 2 — Разверните Milvus в автономном режиме

**Docker-образ:**

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

Для Milvus в автономном режиме требуются etcd и MinIO. Используйте Docker Compose для простейшей настройки.

**Порты:**

```
22
19530
```

* **Порт 19530:** Порт Milvus SDK/gRPC (основной)
* **Порт 9091:** REST API Milvus и проверка здоровья (внутренний)

**Переменные окружения:**

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

***

## Шаг 3 — Настройка с Docker Compose

Подключитесь по SSH к вашему серверу Clore.ai и создайте файл compose:

```bash
ssh root@<server-ip> -p <ssh-port>

# Установите Docker Compose, если он не установлен
which docker-compose || pip install docker-compose
# Или используйте плагин Docker:
docker compose version

# Создайте директорию проекта
mkdir -p /opt/milvus && cd /opt/milvus

# Скачайте официальный файл compose для Milvus standalone
wget https://github.com/milvus-io/milvus/releases/download/v2.4.0/milvus-standalone-docker-compose.yml \
    -O docker-compose.yml

# Просмотрите файл compose
cat docker-compose.yml
```

### Настройте 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"]
      interval: 30s
      timeout: 20s
      retries: 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"]
      interval: 30s
      timeout: 20s
      retries: 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"]
      interval: 30s
      start_period: 90s
      timeout: 20s
      retries: 3
    ports:
      - "19530:19530"
      - "9091:9091"
    depends_on:
      - "etcd"
      - "minio"
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu]  # Включить доступ к GPU
```

### Запустите Milvus

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

# Подождите, пока сервисы запустятся (~60 секунд)
sleep 60

# Проверьте, что все сервисы здоровы
docker compose ps

# Проверьте состояние Milvus
curl http://localhost:9091/healthz
# Ожидаемый ответ: {"status":"ok"}

# Просмотр логов
docker compose logs -f standalone --tail 50
```

***

## Шаг 4 — Установите клиент Python

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

# Проверить соединение
python3 << 'EOF'
from pymilvus import connections, utility

connections.connect("default", host="localhost", port="19530")
print(f"Milvus connected!")
print(f"Version: {utility.get_server_version()}")
EOF
```

***

## Шаг 5 — Создайте коллекцию

В Milvus **collection** похож на таблицу базы данных. У него есть схема с типизированными полями, включая векторные поля.

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

# Подключиться
connections.connect("default", host="localhost", port="19530")

# Определить схему
fields = [
    FieldSchema(
        name="id",
        dtype=DataType.INT64,
        is_primary=True,
        auto_id=True           # Автогенерация ID
    ),
    FieldSchema(
        name="text",
        dtype=DataType.VARCHAR,
        max_length=2048        # Максимальная длина текста
    ),
    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                # Размерность вашей модели эмбеддингов
    )
]

schema = CollectionSchema(
    fields=fields,
    description="Эмбеддинги документов для семантического поиска",
    enable_dynamic_field=True  # Разрешить добавление полей, не указанных в схеме
)

# Создать коллекцию
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"Collection '{collection_name}' created!")
```

***

## Шаг 6 — Создание индекса

Перед загрузкой данных для поиска создайте соответствующий индекс:

```python
from pymilvus import Collection

collection = Collection("documents")

# Индекс HNSW (лучше для большинства случаев, низкая задержка)
hnsw_params = {
    "metric_type": "COSINE",     # COSINE, L2 или IP (скалярное произведение)
    "index_type": "HNSW",
    "params": {
        "M": 16,                 # Связанность графа HNSW (8-64)
        "efConstruction": 200    # Глубина поиска при построении
    }
}

# Индекс IVF_FLAT (CPU, подходит для больших коллекций)
ivf_params = {
    "metric_type": "COSINE",
    "index_type": "IVF_FLAT",
    "params": {
        "nlist": 1024            # Количество кластеров (обычно sqrt от размера данных)
    }
}

# Индекс GPU_IVF_FLAT (требует CUDA GPU — самый быстрый для пакетных запросов)
gpu_ivf_params = {
    "metric_type": "L2",
    "index_type": "GPU_IVF_FLAT",
    "params": {
        "nlist": 1024,
        "cache_dataset_on_device": True
    }
}

# Создать индекс по полю embedding
collection.create_index(
    field_name="embedding",
    index_params=hnsw_params,
    index_name="embedding_idx"
)

# Создать скалярный индекс для фильтруемого поиска
collection.create_index(field_name="category", index_name="category_idx")
collection.create_index(field_name="year", index_name="year_idx")

print("Индексы созданы!")
collection.load()  # Загрузить в память для поиска
```

***

## Шаг 7 — Вставка данных

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

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

# Ваши документы
documents = [
    {
        "text": "Milvus — это открытая векторная база данных для масштабируемых AI-приложений.",
        "source": "documentation",
        "category": "database",
        "year": 2024
    },
    {
        "text": "HNSW обеспечивает быстрый приближённый поиск ближайших соседей с высокой полнотой.",
        "source": "research",
        "category": "algorithm",
        "year": 2023
    },
    {
        "text": "Индексирование с ускорением на GPU значительно сокращает время построения для больших векторных коллекций.",
        "source": "blog",
        "category": "performance",
        "year": 2024
    },
    # Добавьте здесь ещё тысячи документов
]

def insert_batch(docs: list, batch_size: int = 1000):
    texts = [d["text"] for d in docs]
    
    # Ускоренное на GPU построение эмбеддингов
    embeddings = model.encode(
        texts,
        batch_size=256,
        show_progress_bar=False,
        normalize_embeddings=True
    )
    
    # Вставка в 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

# Вставка пакетами
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")

# Флаш, чтобы данные были сохранены и проиндексированы
collection.flush()
print(f"Total inserted and flushed: {total_inserted}")
```

***

## Шаг 8 — Поиск и запросы

### Базовый семантический поиск

```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}    # Параметр HNSW для времени поиска (ef >= top_k)
        },
        limit=top_k,
        output_fields=["text", "source", "category", "year"]
    )
    
    return results[0]

# Поиск
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()
```

### Фильтрованный поиск

```python
from pymilvus import Collection

collection = Collection("documents")

# Поиск с фильтром по метаданным (булево выражение)
results = collection.search(
    data=[query_embedding],
    anns_field="embedding",
    param={"metric_type": "COSINE", "params": {"ef": 64}},
    limit=10,
    expr='category == "database" and year >= 2023',  # Булев фильтр
    output_fields=["text", "category", "year"]
)
```

### Гибридный поиск (плотный + разреженный)

```python
# Milvus 2.4+ поддерживает гибридный dense+sparse поиск
from pymilvus import AnnSearchRequest, WeightedRanker, Collection

collection = Collection("documents")

# Запрос для плотного поиска
dense_req = AnnSearchRequest(
    data=[dense_embedding],
    anns_field="embedding",
    param={"metric_type": "COSINE", "params": {"ef": 64}},
    limit=20
)

# Запрос для разреженного поиска (требуется поле со разреженным вектором)
sparse_req = AnnSearchRequest(
    data=[sparse_embedding],
    anns_field="sparse_embedding",
    param={"metric_type": "IP"},
    limit=20
)

# Комбинировать с Reciprocal Rank Fusion
results = collection.hybrid_search(
    [dense_req, sparse_req],
    rerank=WeightedRanker(0.7, 0.3),  # 70% плотный, 30% разреженный
    limit=10,
    output_fields=["text"]
)
```

***

## Шаг 9 — Постройте 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")

# Инициализация при старте
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": "Answer based on context. Be concise."},
            {"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
```

***

## Шаг 10 — Мониторинг и управление

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

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

# Список всех коллекций
print("Collections:", utility.list_collections())

# Статистика по коллекции
col = Collection("documents")
print(f"Entity count: {col.num_entities:,}")
print(f"Schema: {col.schema}")

# Управление разделами (partitions)
col.create_partition("2024_docs")
col.create_partition("2023_docs")

# Вставка с указанием раздела
col.insert(data, partition_name="2024_docs")

# Поиск в конкретном разделе
results = col.search(
    data=[query_vec],
    anns_field="embedding",
    param={"metric_type": "COSINE", "params": {"ef": 64}},
    limit=10,
    partition_names=["2024_docs"]  # Поиск только в этом разделе
)
```

***

## Устранение неполадок

### Сервисы не запускаются

```bash
# Проверьте логи контейнеров
docker compose logs etcd
docker compose logs minio
docker compose logs standalone

# Проверьте место на диске
df -h /opt/milvus

# Перезапустите сервисы
docker compose restart
```

### Connection Refused на 19530

```bash
# Убедитесь, что Milvus прослушивает порт
netstat -tlnp | grep 19530

# Проверить состояние
curl http://localhost:9091/healthz

# Дайте время на старт (90 секунд)
docker compose logs standalone | tail -20
```

### Таймаут построения индекса для больших коллекций

```python
# Увеличьте таймаут для крупных построений индекса
from pymilvus import Collection

collection = Collection("documents")
collection.create_index(
    field_name="embedding",
    index_params=hnsw_params,
    timeout=3600  # таймаут 1 час
)
```

### Высокое использование памяти

```bash
# Настройте лимиты памяти Milvus в docker-compose.yml
# Добавьте в сервис standalone:
deploy:
  resources:
    limits:
      memory: 16g
```

***

## Руководство по выбору типа индекса

| Тип индекса    | Лучше всего для             | Память           | Скорость     | Требуется GPU |
| -------------- | --------------------------- | ---------------- | ------------ | ------------- |
| FLAT           | Малые (<1M), точный поиск   | Высокая          | Медленно     | Нет           |
| IVF\_FLAT      | Средние (1M–10M)            | Средне           | Хорошо       | Нет           |
| HNSW           | Низкая задержка, <100M      | Высокая          | Отлично      | Нет           |
| IVF\_SQ8       | Сжатый, для больших объёмов | Низкая           | Хорошо       | Нет           |
| GPU\_IVF\_FLAT | Быстрые пакетные запросы    | GPU+ОЗУ          | Лучший выбор | Да            |
| DISKANN        | Для миллиардных объёмов     | Низко (на диске) | Хорошо       | Нет           |

***

## Тесты производительности

| Размер коллекции | Индекс         | GPU      | QPS      |
| ---------------- | -------------- | -------- | -------- |
| 1M векторов      | HNSW           | RTX 3090 | \~8,000  |
| 10M векторов     | IVF\_FLAT      | RTX 4090 | \~2,500  |
| 10M векторов     | GPU\_IVF\_FLAT | A100     | \~12,000 |
| 100M векторов    | DISKANN        | A100     | \~1,200  |

***

## Дополнительные ресурсы

* [Документация Milvus](https://milvus.io/docs)
* [Milvus на GitHub](https://github.com/milvus-io/milvus)
* [Документация PyMilvus](https://milvus.io/api-reference/pymilvus/v2.4.x/About.md)
* [Milvus Bootcamp](https://github.com/milvus-io/bootcamp) — Примеры приложений
* [Zilliz Cloud](https://cloud.zilliz.com/) — Управляемый Milvus
* [Сравнение векторных баз данных](https://milvus.io/docs/benchmark.md)
* [Attu GUI](https://github.com/zilliztech/attu) — Веб-интерфейс для управления Milvus

***

*Milvus на Clore.ai — идеальное решение для AI-приложений, которым нужно масштабироваться за пределы сотен миллионов векторов. В сочетании с генерацией эмбеддингов с ускорением на GPU вы можете создавать первоклассные системы семантического поиска и RAG при доле затрат управляемого облака.*

***

## Рекомендации Clore.ai по GPU

| Сценарий использования                   | Рекомендуемый GPU | Примерная стоимость на Clore.ai |
| ---------------------------------------- | ----------------- | ------------------------------- |
| Разработка/Тестирование                  | RTX 3090 (24GB)   | \~$0.12/gpu/hr                  |
| Производственный векторный поиск         | RTX 3090 (24GB)   | \~$0.12/gpu/hr                  |
| Высокопропускная способность эмбеддингов | RTX 4090 (24GB)   | \~$0.70/gpu/hr                  |

> 💡 Все примеры в этом руководстве можно развернуть на [Clore.ai](https://clore.ai/marketplace) GPU-серверах. Просматривайте доступные GPU и арендуйте по часам — без обязательств, с полным root-доступом.
