임베딩 모델 교체 시 벡터 재색인: 이중 인덱스와 점진 전환 순서

왜 섞어 쓸 수 없는지, pgvector에서 새 컬럼·인덱스를 만드는 순서, 검색 평가로 비교하고 트래픽을 옮기는 절차

임베딩 모델 교체와 재색인 썸네일

핵심 요약

  1. 다른 모델의 임베딩은 같은 공간이 아니므로 질문과 문서 임베딩을 모두 같은 모델로 다시 만들어야 한다.
  2. pgvector의 vector 타입 인덱스는 2,000차원까지라 3072차원 모델은 dimensions로 줄이거나 half-precision을 검토한다.
  3. 새 컬럼 추가 → 백필 + 쓰기 이중화 → CONCURRENTLY 인덱스 → 평가 → 읽기 전환 → 정리 순서로 진행한다.
  4. 읽기 전환 시 검색 컬럼과 질문 임베딩 모델을 한 설정값으로 함께 바꾼다.
  5. 이전 컬럼과 쓰기 이중화는 되돌릴 일이 없다고 판단될 때까지 유지한다.

더 좋은 임베딩 모델이 나왔거나 비용 때문에 모델을 바꾸려 할 때, 새 문서만 새 모델로 넣고 기존 벡터는 그대로 두면 검색이 조용히 망가진다. 이 글은 운영 중인 RAG에서 임베딩 모델을 안전하게 교체하는 순서를 다룬다. 예시는 PostgreSQL + pgvector 기준이지만 다른 벡터 DB에서도 "새 인덱스를 따로 만들고, 평가하고, 읽기를 옮기고, 이전 것을 지운다"는 흐름은 같다.

기존 벡터와 새 벡터를 섞으면 안 되는 이유

임베딩 벡터의 각 숫자는 그 모델이 학습한 공간에서만 의미가 있다. 모델 A가 만든 질문 벡터와 모델 B가 만든 문서 벡터의 코사인 유사도는 차원 수가 같더라도 비교할 근거가 없다. 차원이 다르면 아예 계산이 되지 않고, 같으면 오류 없이 엉뚱한 결과가 나와서 더 위험하다. 따라서 모델을 바꾸면 질문 임베딩과 모든 문서 임베딩을 같은 모델로 다시 만들어야 한다. 유사도 계산 원리는 벡터 임베딩과 코사인 유사도에서 설명했다.

교체 전에 계산할 것

항목확인 방법왜 필요한가
새 모델의 차원 수모델 문서. 예: OpenAI text-embedding-3-small 기본 1536, text-embedding-3-large 기본 3072컬럼 타입과 인덱스 가능 여부 결정
pgvector 인덱스 한도vector 타입은 2,000차원까지, half-precision은 4,000차원까지3072차원은 그대로는 인덱스 불가
최대 입력 길이예: OpenAI 3세대 모델 8192 토큰기존 청크가 새 모델 한도를 넘는지
재색인 토큰 수전체 청크 토큰 합계비용과 소요 시간 추정

값은 pgvector README와 OpenAI 임베딩 가이드 기준이다. 3072차원 모델을 pgvector HNSW 인덱스와 함께 쓰려면 OpenAI 문서가 안내하는 dimensions 파라미터로 차원을 줄여 받거나, half-precision 타입을 쓰는 방법이 있다. 문서에 따르면 dimensions로 줄여 받으면 개념 표현 성질이 유지되며, 받은 뒤 직접 잘라낼 경우에는 다시 정규화해야 한다.

이중 인덱스로 재색인하는 순서

  1. 새 컬럼 추가 — 기존 embedding 컬럼은 그대로 두고 새 모델용 컬럼을 추가한다. 이 단계에서는 읽기 경로를 바꾸지 않는다.
  2. 백필 — 배치로 청크를 읽어 새 모델로 임베딩하고 새 컬럼에 쓴다. 진행 위치를 기록해 중단돼도 이어서 할 수 있게 한다.
  3. 쓰기 이중화 — 백필 중 새로 들어오는 문서는 두 모델로 모두 임베딩해 두 컬럼에 쓴다. 이걸 빼먹으면 백필이 끝난 뒤 새 컬럼에 빈 행이 남는다.
  4. 새 인덱스 생성 — 백필이 끝나면 새 컬럼에 인덱스를 만든다. README는 운영 환경에서 쓰기를 막지 않도록 동시 생성을 권한다.
  5. 평가 — 같은 평가셋으로 두 컬럼의 검색 성능을 비교한다.
  6. 읽기 전환 — 설정값 하나로 검색에 쓸 컬럼과 질문 임베딩 모델을 함께 바꾼다. 둘 중 하나만 바뀌면 앞에서 말한 섞임 문제가 생긴다.
  7. 정리 — 되돌릴 일이 없다고 판단되는 기간이 지나면 이전 컬럼과 인덱스, 쓰기 이중화 코드를 제거한다.
-- 1) 새 컬럼 (예: 1024차원 모델)
ALTER TABLE chunks ADD COLUMN embedding_v2 vector(1024);
ALTER TABLE chunks ADD COLUMN embedding_v2_model text;

-- 2) 백필 진행 확인
SELECT count(*) FILTER (WHERE embedding_v2 IS NULL) AS remaining,
       count(*) AS total
FROM chunks;

-- 4) 새 인덱스를 쓰기 차단 없이 생성
CREATE INDEX CONCURRENTLY chunks_embedding_v2_hnsw
  ON chunks USING hnsw (embedding_v2 vector_cosine_ops);

새 컬럼 추가와 이전 컬럼 삭제를 나눠 배포하는 방식은 일반적인 무중단 스키마 변경 순서와 같다. 잠금 주의점은 PostgreSQL 무중단 스키마 변경에서 다뤘다.

백필 스크립트 뼈대

import time

BATCH = 200

def backfill(conn, embed_v2):
    while True:
        rows = conn.execute(
            'SELECT id, content FROM chunks WHERE embedding_v2 IS NULL ORDER BY id LIMIT %s',
            (BATCH,)).fetchall()
        if not rows:
            break
        vectors = embed_v2([r[1] for r in rows])        # 새 모델 배치 호출
        with conn.transaction():
            for (cid, _), vec in zip(rows, vectors):
                conn.execute(
                    'UPDATE chunks SET embedding_v2 = %s, embedding_v2_model = %s WHERE id = %s',
                    (vec, 'new-embed-model', cid))
        time.sleep(0.5)                                  # 제공자 속도 제한 고려

embedding_v2_model 컬럼에 모델 이름을 남겨 두면 나중에 일부 행만 다른 모델로 들어간 사고를 쿼리 하나로 찾을 수 있다. 백필이 끝났다고 판단하기 전에 SELECT DISTINCT embedding_v2_model로 값이 하나뿐인지 확인한다.

전환 여부를 숫자로 판단하기

새 모델이 더 좋다는 일반적인 평가는 우리 문서와 질문에서의 결과를 보장하지 않는다. RAG 검색 단계 평가 스크립트를 검색 함수만 바꿔 두 번 실행하고, recall@k와 MRR, 그리고 "이전 모델은 찾았는데 새 모델이 놓친 질문" 목록을 비교한다. 놓친 질문이 특정 유형(약어, 표 데이터, 영문 혼용)에 몰려 있다면 전환 전에 청킹이나 키워드 검색 병행으로 보완할지 판단한다.

결과판단
recall·MRR 상승, 새로 놓친 질문 거의 없음읽기 전환
평균은 비슷, 놓친 질문 유형이 바뀜놓친 질문을 직접 읽고 중요도로 판단
평균 하락전환 보류. 차원 축소 값이나 청크 길이를 바꿔 재평가

되돌리기 경로를 남겨 두기

읽기 전환 뒤 실제 사용자 질문에서 문제가 보일 수 있으므로, 이전 컬럼·인덱스와 쓰기 이중화는 일정 기간 유지한다. 전환은 설정값 하나로 되돌릴 수 있어야 한다. 벡터 DB 자체를 바꾸는 판단은 pgvector, Pinecone, Milvus 비교를 참고한다. 이중 보관 기간에는 저장 공간과 인덱스 메모리가 두 배 가까이 필요하므로 DB 용량도 미리 확인한다.

초보자가 자주 실수하는 포인트

  • 새 문서만 새 모델로 넣고 기존 벡터와 섞어 검색하는 경우
  • 백필 중 들어온 새 문서를 이전 모델로만 임베딩해 새 컬럼에 빈 행이 남는 경우
  • 인덱스를 CONCURRENTLY 없이 만들어 운영 중 쓰기가 막히는 경우
  • 평균 점수만 보고 전환해 특정 유형 질문의 검색 실패를 놓치는 경우

체크리스트

  • 새 모델의 차원 수와 인덱스 한도를 확인했는가
  • 백필 중 신규 문서 쓰기 이중화가 적용됐는가
  • 새 컬럼의 NULL 행과 모델 이름 값이 하나뿐인지 확인했는가
  • 같은 평가셋으로 두 모델을 비교했는가
  • 읽기 전환을 설정값 하나로 되돌릴 수 있는가

자주 묻는 질문

모델 제공자가 같은 이름으로 모델을 업데이트하면 재색인이 필요한가요?

이름이 같아도 벡터 공간이 바뀌었다면 재색인이 필요합니다. 제공자의 변경 공지를 확인하고, 불확실하면 고정된 문서 몇 개를 다시 임베딩해 저장된 벡터와 비교하는 점검 작업을 정기적으로 돌리는 방법이 있습니다.

재색인 비용은 어떻게 추정하나요?

전체 청크의 토큰 수 합계에 새 모델의 입력 단가를 곱합니다. 토큰 수는 표본 청크 수백 개를 해당 모델의 토크나이저로 세어 평균을 낸 뒤 전체 청크 수를 곱해 근사할 수 있습니다.

참고 자료 · 검증 기준

위 자료와 내용을 대조한 날짜: . 도구·서비스 정책은 이후 바뀔 수 있으므로 적용 전 공식 문서를 다시 확인하세요.

이 글은 실제 실무 환경 테스트 및 검증된 기술 자료를 바탕으로 작성되었으며, 환경 변화에 따라 지속적으로 갱신됩니다.