마이그레이션 전 기준점
마이그레이션 전 기준점 (Baseline) 잡기 (migration-guidance-pre-migration-baseline)
다른 벡터 DB에서 Qdrant로 마이그레이션할 때, 가장 먼저 해야 할 일은 '올바른 결과가 무엇인지'를 확실히 기록해 두는 거예요. 기준점(baseline)을 잡는 것이 마이그레이션 검증의 핵심인데, 마이그레이션하기 전에 "정답"이 무엇인지 포착해 두지 않으면 나중에 비교할 대상이 없어지거든요. 이 페이지에서는 마이그레이션을 시작하기 전에 소스 시스템에서 무엇을 기록해 둬야 하는지 다룰게요.
출처: Qdrant 공식문서
무엇을 포착해야 하나요?
기준점을 잡을 때 고려해야 할 정보는 네 가지예요. 컬렉션/인덱스 인벤토리, 메타데이터 샘플, 기준 검색 결과, 그리고 시스템 설정 스냅샷이에요.
1. 컬렉션/인덱스 인벤토리
마이그레이션할 각 인덱스/컬렉션마다 다음 정보를 기록해요.
For each collection: - Name / identifier - Vector count - Vector dimensions - Distance metric (cosine, dot product, euclidean) - Index type and parameters (e.g., HNSW ef_construction, M) - Quantization settings (if any) - Replication factor (if applicable)
거리 메트릭(distance metric)에 특히 주의를 기울여야 해요. 거리 메트릭 불일치는 마이그레이션 후 검색 품질이 저하되는 가장 흔한 원인 중 하나예요. 코사인 유사도, 내적(dot product), 유클리드 거리는 같은 벡터에서도 서로 다른 순위를 만들어내요. 소스 시스템이 코사인을 쓰는데 실수로 Qdrant를 내적으로 설정한다면 모든 검색 결과가 바뀌어 버려요.
Pinecone
# Pinecone baseline capture import pinecone # Record index stats index = pinecone . Index ( "your-index" ) stats = index . describe_index_stats () baseline = { "total_vector_count" : stats . total_vector_count , "dimension" : stats . dimension , "namespaces" : { ns : { "vector_count" : ns_stats . vector_count } for ns , ns_stats in stats . namespaces . items () }, # Pinecone doesn't expose distance metric via API; # check your index creation code or dashboard "metric" : "cosine" , # VERIFY THIS MANUALLY }
Weaviate
# Weaviate baseline capture import weaviate client = weaviate . Client ( "http://localhost:8080" ) schema = client . schema . get () for cls in schema [ "classes" ]: baseline = { "class_name" : cls [ "class" ], "vector_count" : client . query . aggregate ( cls [ "class" ]) . with_meta_count () . do (), "distance_metric" : cls . get ( "vectorIndexConfig" , {}) . get ( "distance" , "cosine" ), "ef_construction" : cls . get ( "vectorIndexConfig" , {}) . get ( "efConstruction" ), "vector_dimensions" : None , # Weaviate infers from data; check a sample vector }
Milvus / Zilliz
# Milvus baseline capture from pymilvus import connections , Collection connections . connect ( "default" , host = "localhost" , port = "19530" ) collection = Collection ( "your_collection" ) collection . load () baseline = { "collection_name" : collection . name , "num_entities" : collection . num_entities , "schema_fields" : [ { "name" : f . name , "dtype" : str ( f . dtype ), "dim" : getattr ( f , "dim" , None )} for f in collection . schema . fields ], "index_params" : collection . indexes , # Capture index type + params }
Elasticsearch
# Elasticsearch baseline capture from elasticsearch import Elasticsearch es = Elasticsearch ( "http://localhost:9200" ) # Get mapping to find vector field config mapping = es . indices . get_mapping ( index = "your_index" ) stats = es . count ( index = "your_index" ) baseline = { "index_name" : "your_index" , "document_count" : stats [ "count" ], "mapping" : mapping , # Contains vector field type, dims, similarity metric }
pgvector
-- pgvector baseline capture SELECT relname AS table_name , n_live_tup AS approximate_row_count FROM pg_stat_user_tables WHERE relname = 'your_embeddings_table' ; -- Vector dimensions (check first row) SELECT vector_dims ( embedding ) FROM your_embeddings_table LIMIT 1 ; -- Index configuration SELECT indexname , indexdef FROM pg_indexes WHERE tablename = 'your_embeddings_table' ; -- Distance metric: check your index definition -- ivfflat with vector_cosine_ops = cosine -- ivfflat with vector_l2_ops = euclidean -- ivfflat with vector_ip_ops = inner product (dot)
2. 메타데이터 샘플
소스 시스템에서 대표적인 메타데이터(또는 페이로드) 샘플을 추출해요. 마이그레이션 후 필드 단위 비교에 이걸 사용할 거예요.
얼마나 샘플링할까요: 최소 1,000개 레코드 또는 데이터의 1% 중 더 큰 쪽으로요. 10만 벡터 미만의 데이터셋이라면 메타데이터 전체를 추출하는 것도 고려해 보세요.
각 샘플에 기록할 내용:
- Point/document ID - All metadata fields with their values - Metadata field types (string, integer, float, boolean, array, nested object) - Any null/missing fields (important: some systems drop nulls on export)
메타데이터 유형 강제 변환(type coercion)은 아주 은근한 마이그레이션 실패 지점이에요. Pinecone에서 정수로 저장된 필드가 Qdrant에서는 float로 도착할 수 있어요. Elasticsearch에서 "true"(문자열)로 저장된 boolean은 명시적인 타입 처리가 필요해요. 이런 불일치는 가져올 때 오류를 일으키지 않지만, 필터링된 검색 쿼리를 깨뜨려요.
3. 기준 검색 쿼리
잡아둘 수 있는 가장 가치 있는 기준점은 검색 품질이에요. 실제 검색 워크로드를 대표하는 쿼리 10~50개를 선정해요.
# Structure for recording baseline queries baseline_queries = [ { "query_id" : "q001" , "description" : "Product search: running shoes" , "query_vector" : [ ... ], # The actual query vector "filters" : { "category" : "footwear" , "in_stock" : True }, # If applicable "top_k" : 10 , "source_results" : [ { "id" : "doc_123" , "score" : 0.95 , "rank" : 1 }, { "id" : "doc_456" , "score" : 0.91 , "rank" : 2 }, # ... full top-k ], "timestamp" : "2026-03-10T14:30:00Z" , "source_system" : "pinecone" , "source_index" : "products-v2" , }, ]
대표 쿼리를 어떻게 고를까요:
- 가장 빈번한 프로덕션 쿼리를 포함한다 (로그를 확인해요)
- 엣지 케이스를 포함한다: 선택성이 높은 필터가 있는 쿼리, 결과를 거의 반환하지 않는 쿼리, 여러 데이터 유형에 걸친 쿼리
- 벡터 공간의 다른 부분에서 온 쿼리를 포함한다 (비슷한 쿼리 한 구역만 테스트하지 말고 여러 클러스터에 걸쳐 테스트해요)
- 하이브리드 검색(dense + sparse)을 쓴다면 두 구성 요소를 모두 포착해요
각 쿼리에 기록할 내용:
- 쿼리 벡터 자체 (재임베딩하지 않은 정확한 float 값)
- 적용된 메타데이터 필터
- 사용된 top-k 값
- 점수가 포함된 전체 순위 결과 목록
- 리랭킹이 적용되었는지 여부
4. 시스템 설정 스냅샷
검색 동작에 영향을 주는 소스 시스템의 설정을 기록해요.
- Software version (e.g., Pinecone API version, Weaviate 1.24, Milvus 2.3) - Index/collection creation parameters - Quantization settings (PQ, SQ, none) - HNSW parameters (ef_construction, M, ef_search) if applicable - Segment/shard configuration - Any custom scoring, re-ranking, or post-processing logic - Client library version
마이그레이션 후 검색 결과가 다르다면, 그 차이가 데이터에서 왔는지 설정에서 왔는지 판단해야 해요. 설정 스냅샷이 없으면 "벡터가 잘못 마이그레이션됐다"와 "인덱싱 파라미터가 다른 재현율 특성을 만든다"를 구분할 수 없어요.
결과물 (Output)
이 단계를 마치면 네 가지 산출물을 갖추게 돼요.
- 컬렉션 인벤토리 (JSON 또는 YAML): 이름, 수, 차원, 메트릭, 인덱스 파라미터
- 메타데이터 샘플 (JSONL): 모든 필드와 유형을 포함한 대표 레코드
- 기준 쿼리 (JSON): 쿼리 벡터, 필터, 소스 시스템 결과
- 설정 스냅샷 (텍스트): 검색 동작에 영향을 주는 소스 시스템 설정
이것들을 마이그레이션 스크립트와 함께 보관해요. 이후의 모든 검증 단계에서 참조하게 될 거예요.