codingstairs
노트에듀라이프연락
⌕검색⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

연락하기

로그인 없이도 보낼 수 있어요. 답변이 필요하면 이메일을 함께 적어 주세요.

  • 익명 폼으로 의견 남기기 →
  • ✉ warragon112@gmail.com
  • 카카오톡 오픈채팅 ↗

© 2026 codingstairs

  • 노트
  • 에듀
  • 검색
  • 라이프
  • 연락
  • 약관
  • RSS
  • GitHub
에듀›PostgreSQL 깊게 다루기 + Redis · Kafka›3단계

3단계

pgvector + HNSW

0회 조회

pgvector + HNSW

PostgreSQL 이 벡터 검색까지 처리. 전용 벡터 DB 없이 "충분히 좋음".

1. 설치

docker run -d -e POSTGRES_PASSWORD=x -p 5432:5432 pgvector/pgvector:pg16

기존 PG:

CREATE EXTENSION IF NOT EXISTS vector;

2. 테이블

CREATE TABLE document_chunks (
  id          BIGSERIAL PRIMARY KEY,
  document_id BIGINT NOT NULL,
  chunk_index INT NOT NULL,
  content     TEXT NOT NULL,
  embedding   vector(768),
  created_at  TIMESTAMPTZ DEFAULT now()
);

vector(N) 차원은 임베딩 모델과 일치. 변경 시 재빌드.

3. 인덱스 선택 — HNSW vs IVFFlat

인덱스 빌드 쿼리 정확도 용도
HNSW 느림 빠름 매우 높음 읽기 중심 RAG
IVFFlat 빠름 중간 튜닝 필요 대량 적재 + 주기 재빌드

4. HNSW 생성

CREATE INDEX ON document_chunks
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
  • m — 그래프 이웃 수 (기본 16). 높이면 정확 · 메모리 ↑
  • ef_construction — 빌드 시 탐색 폭 (64). 높이면 느리지만 정확 ↑

전체 적재 후 한 번에 생성이 빠름.

5. 검색

SELECT id, content, 1 - (embedding <=> $1::vector) AS similarity
FROM document_chunks
ORDER BY embedding <=> $1::vector
LIMIT 10;
  • <=> — 코사인 거리 (0 = 동일)
  • <-> — L2
  • <#> — 음수 내적

반드시 ORDER BY <거리> ASC + LIMIT k 여야 인덱스 사용.

6. 런타임 튜닝

SET hnsw.ef_search = 100;   -- 기본 40

높이면 정확 · 느림. 세션 단위.

7. 배치 적재

async function indexChunks(chunks: { id: number; embedding: number[] }[]) {
  const values = chunks.map((_, i) =>
    `($${i*2+1}, $${i*2+2}::vector)`
  ).join(", ");
  const params = chunks.flatMap(c => [c.id, JSON.stringify(c.embedding)]);
  await pool.query(
    `UPDATE document_chunks SET embedding = v.e
     FROM (VALUES ${values}) AS v(id, e)
     WHERE document_chunks.id = v.id::bigint`,
    params
  );
}

50 ~ 100 개 batch. API rate limit 주의.

8. 하이브리드 검색 — pg_trgm + vector

SELECT *, similarity(content, $1) AS text_score,
       1 - (embedding <=> $2::vector) AS vec_score
FROM document_chunks
ORDER BY (similarity(content, $1) * 0.3 + (1 - (embedding <=> $2::vector)) * 0.7) DESC
LIMIT 10;

텍스트 정확 일치 + 의미 유사도 혼합. 정확도 보정 효과.

9. 백업

pg_dump 가 벡터를 텍스트 덤프. 복원 시 인덱스 재빌드 필요 (시간 소요). 대용량은 pg_restore --jobs 4 병렬.

10. 자주 걸리는 자리

  • CREATE EXTENSION vector 누락
  • similarity DESC 로 정렬 — HNSW 인덱스 미사용. 거리 ASC 로
  • 차원 불일치 — 모델 바꿨는데 컬럼 그대로
  • EXPLAIN 에서 Seq Scan — 인덱스 작동 안 함. 쿼리 패턴 점검

11. 한계 — 언제 전용 벡터 DB?

규모 권장
~10M 벡터 pgvector 여유
10M ~ 100M pgvector 튜닝 (파티셔닝 · 샤딩)
100M+ Qdrant · Milvus · Vespa

대부분의 프로젝트는 10M 미만에서 끝.

하고픈 말

pgvector 로 시작하고 병목 측정 후 전환이 올바른 순서. "처음부터 Qdrant" 는 대개 과잉.

Next

  • 04-redis-five-roles

← 2단계

여러 풀 오케스트레이션

4단계 →

Redis 의 역할 5가지