데이터 엔지니어 · AI 엔지니어를 위한 핵심 요약
Milvus는 대규모 벡터 데이터를 저장, 관리, 검색하기 위해 설계된 오픈소스 벡터 데이터베이스입니다. AI 애플리케이션(유사성 검색, 추천 시스템, 멀티모델 검색 등)의 핵심 인프라 역할을 합니다.
억(10^9) 단위의 고차원 벡터에서도 밀리초 수준의 Nearest Neighbor Search(NNS)를 지원합니다. 분산 아키텍처로 스케일 아웃이 가능합니다.
IVF_FLAT, HNSW, DISKANN, SCANN 등 상황별 최적의 인덱스 알고리즘을 제공하여 정확도/속도/메모리 trade-off를 조정할 수 있습니다.
벡터 검색과 메타데이터 필터링(속성 검색)을 결합한 강력한 하이브리드 쿼리를 지원합니다. 실무에서 필수적인 기능입니다.
AI 애플리케이션에서 벡터 검색을 구현할 때 고려할 수 있는 대안들과 Milvus의 비교입니다.
| 솔루션 | 유형 | 장점 | 단점 / Milvus 차별점 | 적합한 경우 |
|---|---|---|---|---|
| Milvus | 전용 벡터 DB | 대규모, 고성능, 분산, 다양한 인덱스, 하이브리드 검색 | 운영 복잡성(클러스터), 전문성 요구 | 프로덕션 대규모 시스템, 실시간 검색 |
| PostgreSQL + pgvector | DB 확장 | SQL 통합, 운영 간편, 트랜잭션 지원 | 스케일 제한, 인덱스 옵션 부족, 전문 검색 성능 낮음 | 소규모, 벡터+메타데이터 통합 관리가 주요 요구 |
| Elasticsearch | 검색 엔진 | 풍부한 텍스트 검색, 강력한 집계, 운영 도구 많음 | 벡터 검색 기능 제한적(8.0+ 지원), 전문 벡터 인덱스 부재 | 텍스트 검색이 주이고 벡터 검색이 보조인 경우 |
| 메모리/파일 기반 (FAISS, Annoy) | 라이브러리 | 단순함, 빠른 프로토타이핑, 연구용 | 지속성 없음, 분산 불가, 운영 기능(백업/모니터링) 없음 | 연구, 소규모 프로토타입, 임시 검색 |
Standalone 모드로 빠르게 시작하는 단계별 가이드입니다.
# Milvus Standalone Docker 이미지 pull 및 실행
docker pull milvusdb/milvus:latest
docker run -d \
--name milvus-standalone \
-p 19530:19530 \
-p 9091:9091 \
milvusdb/milvus:latest
포트 19530(GRPC)과 9091(HTTP)이 열립니다.
# pymilvus 설치
pip install pymilvus
# Python 연결 코드
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType
# Milvus 서버 연결
connections.connect(
alias="default",
host='localhost',
port='19530'
)
print("Connected to Milvus.")
# 1. 필드 정의 (ID, 벡터, 속성)
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=128),
FieldSchema(name="category", dtype=DataType.VARCHAR, max_length=100)
]
# 2. Collection Schema 생성
schema = CollectionSchema(fields=fields, description="첫 번째 예제 컬렉션")
# 3. Collection 생성
collection_name = "demo_collection"
collection = Collection(name=collection_name, schema=schema)
# 4. 데이터 삽입 (벡터 + 메타데이터)
import random
data = [
[i for i in range(10)], # id 리스트
[[random.random() for _ in range(128)] for _ in range(10)], # 10개의 128차원 벡터
["cat", "dog", "bird", "cat", "dog", "bird", "cat", "dog", "bird", "cat"] # 속성
]
collection.insert(data)
print(f"Inserted {len(data[0])} entities.")
# 5. 인덱스 생성 (HNSW 사용)
index_params = {
"index_type": "HNSW",
"metric_type": "L2",
"params": {"M": 16, "efConstruction": 200}
}
collection.create_index("embedding", index_params)
print("Index created.")
# Collection 로드 (인덱싱 후 필수)
collection.load()
# 검색 벡터 준비
search_vector = [[random.random() for _ in range(128)]]
# 검색 파라미터 설정
search_params = {"metric_type": "L2", "params": {"ef": 50}}
# 하이브리드 쿼리: 벡터 검색 + 속성 필터링
results = collection.search(
data=search_vector,
anns_field="embedding",
param=search_params,
limit=5,
expr="category == 'cat'", # 메타데이터 필터링 표현식
output_fields=["id", "category"] # 반환 필드 지정
)
for hits in results:
for hit in hits:
print(f"id: {hit.entity.id}, category: {hit.entity.category}, distance: {hit.distance}")
Standalone 및 Cluster 모드의 주요 구성 옵션입니다.
| 설정 항목 | 기본값/예시 | 설명 | 실무 영향 |
|---|---|---|---|
| 인덱스 타입 (index_type) | "HNSW", "IVF_FLAT", "DISKANN" | 벡터 검색 알고리즘 선택 | HNSW: 정확도 높음, 메모리 많이 사용. IVF_FLAT: 밸런스 좋음. DISKANN: 디스크 기반 대규모. |
| 인덱스 파라미터 (params) | HNSW: {"M": 16, "efConstruction": 200} | 인덱스 품질/성능/메모리 Trade-off 조정 | M, efConstruction 높으면 정확도 ↑, 빌드 시간 ↑, 메모리 ↑. |
| 검색 파라미터 (search_params) | HNSW: {"ef": 50}, IVF: {"nprobe": 10} | 검색 시 정확도/속도 조정 | ef, nprobe 높으면 정확도 ↑, 검색 시간 ↑. |
| 쓰루풋/병렬처리 (queryNode.replica) | Cluster: 2 | Query Node 복제본 수 | 높으면 쿼리 처리량 ↑, 리소스 사용 ↑. |
| 메타 저장소 (meta.store) | etcd (Cluster), MySQL (Standalone) | Collection, Index, Partition 메타정보 저장 | etcd: 분산 환경 강건성. MySQL: 단순성. |
| 오브젝트 저장소 (minio.address) | localhost:9000 (Standalone) | 실제 벡터 데이터 저장 (S3 호환) | 성능 및 확장성의 핵심. 프로덕션에서는 외부 S3 사용 권장. |
expr)은 벡터 검색 후 적용됩니다. 필터링 조건이 너무 강하면 결과가 적어질 수 있습니다.원인: 인덱스 빌드 파라미터(efConstruction, nlist)가 너무 낮거나, 검색 파라미터(ef, nprobe)가 너무 낮음.
해결: 인덱스 재생성 시 파라미터 증가. 또는 검색 시 ef/nprobe 값을 점진적으로 증가시켜 테스트.
원인: 단일 삽입 작업을 반복 호출, 또는 인덱스가 생성된 상태에서 삽입.
해결: 벡터를 배치로 삽입 (최소 100~1000개 단위). 인덱스 생성 전에 모든 데이터 삽입 완료 후 인덱싱.
원인: 인덱스(특히 HNSW)가 너무 많은 메모리를 요구.
해결: 인덱스 타입을 IVF_FLAT 또는 DISKANN으로 변경. 또는 Worker Node 메모리 증가, 데이터를 여러 파티션으로 분산.