Milvus — распределённая векторная база данных для поиска по смыслу. Она хранит эмбеддинги, строит приближённый индекс ближайших соседей и позволяет комбинировать векторный поиск с обычными условиями по полям. В 2026 году такие базы используют для поиска по документации, рекомендаций, дедупликации и мультимодального поиска, где сравнение «в лоб» по миллионам векторов уже не укладывается в бюджет задержки.
Как устроен Milvus
В распределённом режиме запрос проходит через прокси, который принимает данные и распределяет нагрузку. Координаторы управляют схемой, сегментами и индексами, а узлы query и data выполняют поиск и запись. Метаданные лежат в etcd, сами векторы и индексы — в объектном хранилище S3 или MinIO, поток изменений идёт через журнал WAL на Pulsar или Kafka. Такое разделение позволяет масштабировать чтение и запись независимо.
Для ноутбука и тестов есть Milvus Lite — встраиваемый режим без кластера. Для одного сервера подойдёт Standalone в Docker Compose, для промышленной нагрузки — Kubernetes с Helm или оператором. Начинать почти всегда стоит с Standalone: схема и клиентский код те же, а диагностика проще.
Запуск Standalone
Официальный compose-файл поднимает Milvus, etcd и MinIO. Скачайте его из релизов проекта и запустите:
curl -L -o milvus-standalone-docker-compose.yml \
https://github.com/milvus-io/milvus/releases/latest/download/milvus-standalone-docker-compose.yml
docker compose -f milvus-standalone-docker-compose.yml up -d
curl -s http://localhost:9091/healthz
docker compose -f milvus-standalone-docker-compose.yml ps
Порт 19530 принимает клиентов, 9091 отдаёт метрики и проверку здоровья, 9001 — консоль MinIO. Если healthz отвечает OK, а клиент не подключается, проверьте, что порт опубликован и брандмауэр не блокирует соединение. Не открывайте 19530 в интернет: доступ закрывают сетью и прокси.
Схема коллекции
Коллекция — аналог таблицы. Помимо вектора в ней обычно есть первичный ключ и скалярные поля для фильтрации. Размерность вектора должна совпадать с моделью эмбеддингов: у многих современных моделей это 768 или 1024.
from pymilvus import MilvusClient, DataType
client = MilvusClient(uri="http://localhost:19530")
schema = client.create_schema(auto_id=True)
schema.add_field("id", DataType.INT64, is_primary=True)
schema.add_field("embedding", DataType.FLOAT_VECTOR, dim=768)
schema.add_field("title", DataType.VARCHAR, max_length=512)
schema.add_field("year", DataType.INT32)
Явная схема лучше динамической: она ловит ошибки типов на входе и позволяет строить индексы по скалярным полям.
Индекс и метрика
Индекс определяет компромисс между скоростью, памятью и полнотой. HNSW даёт высокую скорость поиска и требует памяти под граф, IVF_FLAT экономит память, но требует настройки числа списков, DiskANN выносит часть структуры на диск.
index_params = client.prepare_index_params()
index_params.add_index(
field_name="embedding",
index_type="HNSW",
metric_type="COSINE",
params={"M": 16, "efConstruction": 200},
)
client.create_collection("papers", schema=schema, index_params=index_params)
Параметр M задаёт число связей в графе, efConstruction — качество построения. При поиске ef управляет компромиссом: больше значение — выше полнота и задержка. Метрику выбирают под модель: для нормализованных эмбеддингов подходит COSINE или IP, для сырых векторов — L2.
Запись и поиск с фильтром
Вставка принимает список словарей. Индексация выполняется асинхронно, поэтому сразу после вставки данные могут быть ещё не видны поиску.
rows = [
{"embedding": vec, "title": "Kerberos в Linux", "year": 2026},
{"embedding": vec2, "title": "Векторный поиск", "year": 2025},
]
client.insert("papers", rows)
client.flush("papers")
res = client.search(
"papers", data=[query_vec], anns_field="embedding",
limit=10, filter='year >= 2025',
output_fields=["title", "year"],
search_params={"metric_type": "COSINE", "params": {"ef": 64}},
)
Фильтр применяется вместе с поиском, а не после него, поэтому лишние векторы не попадают в топ. Выражения поддерживают сравнения, in, like и логические операторы. Для высокоселективных фильтров полезен partition key: он физически группирует данные и сокращает объём сканирования.
Гибридный поиск
Плотные эмбеддинги плохо ловят точные термины и артикулы, разреженные — наоборот. Гибридный поиск объединяет несколько запросов и ранжирует результаты общим алгоритмом.
from pymilvus import AnnSearchRequest, RRFRanker
dense = AnnSearchRequest(data=[query_vec], anns_field="embedding",
param={"metric_type": "COSINE", "params": {"ef": 64}}, limit=20)
sparse = AnnSearchRequest(data=["kerberos keytab"], anns_field="sparse",
param={"metric_type": "BM25"}, limit=20)
res = client.hybrid_search("papers", [dense, sparse],
ranker=RRFRanker(60), limit=10, output_fields=["title"])
Для гибридной схемы в коллекции нужно несколько векторных полей, а для BM25 — разреженный вектор и полнотекстовый индекс. RRF складывает обратные ранги и не требует калибровки оценок, поэтому подходит как первый вариант. Взвешенное объединение применяют, когда есть размеченная выборка для настройки весов.
Ресурсы и диагностика
Память считается просто: число векторов умножить на размерность и на четыре байта, плюс запас на индекс и служебные структуры. Десять миллионов векторов размерности 768 занимают около 30 ГБ только под сырые данные, а HNSW добавляет граф. Квантизация SQ8 уменьшает объём вчетверо ценой небольшой потери полноты.
docker stats --no-stream
curl -s localhost:9091/metrics | grep milvus_querynode
curl -s localhost:9091/metrics | grep milvus_proxy
client.get_collection_stats("papers")
Смотрите на задержку поиска, долю попаданий в кеш, размер сегментов и очередь запросов. Если задержка растёт вместе с числом сегментов, поможет compaction. Если память упирается в лимит, переходите на DiskANN или квантизацию, а не увеличивайте ef наугад.
Резервное копирование
Объектное хранилище и etcd — не резервная копия. Для согласованного снимка используйте утилиту milvus-backup: она сохраняет коллекции и метаданные в S3-совместимое хранилище.
milvus-backup create -n daily_2026_11_06
milvus-backup list
milvus-backup restore -n daily_2026_11_06 -c papers_restore
Проверяйте восстановление на стенде: неразвёрнутый снимок нельзя считать рабочим. Держите версию схемы и модели эмбеддингов рядом с копией — без них векторы бесполезны.
Чек-лист
- Размерность и метрика совпадают с моделью эмбеддингов.
- Индекс выбран под объём данных и лимит памяти.
- Фильтры проверены на селективность, partition key обоснован.
- Порт базы закрыт снаружи, доступ только через доверенную сеть.
- Настроены метрики, алерты и регулярные проверки восстановления.
Итог
Milvus даёт масштабируемый слой для векторного поиска: отдельные компоненты отвечают за запись, индексацию и чтение, а объектное хранилище снимает ограничение на объём. Начните со Standalone и явной схемы, подберите индекс по замеру полноты и задержки, затем добавляйте фильтры и гибридный поиск. Главные ошибки — несовпадение метрики с моделью и экономия на резервных копиях.