PostgreSQL과 PgVector에 관하여

2026. 4. 10. 14:17·Python

 

1. pgvector란?

pgvector는 PostgreSQL에 벡터 타입과 유사도 검색 기능을 추가하는 오픈소스 확장(extension) 이다.

 

 



별도의 벡터 전용 데이터베이스를 구축하지 않아도, 이미 사용 중인 PostgreSQL에 `CREATE EXTENSION vector;` 한 줄로 벡터 검색 기능을 쓸 수 있게 된다.

 

-- 활성화
CREATE EXTENSION IF NOT EXISTS vector;

-- vector 타입 컬럼 생성
CREATE TABLE items (
id SERIAL PRIMARY KEY,
content TEXT,
embedding vector(1536) -- 1536차원 벡터
);

-- 유사도 검색
SELECT *, embedding <-> '[0.1, 0.2, ...]'::vector AS distance
FROM items
ORDER BY distance
LIMIT 5;



현재 pgvector 0.8 기준으로 최대 16,000차원까지 지원하고,
AWS RDS PostgreSQL 15 이상에서는 기본 탑재되어 있어 `CREATE EXTENSION`만 실행하면 바로 사용 가능하다.

 

 


2. FAISS와 비교 — 언제 pgvector를 선택해야할까?

pgvector를 사용하기 전에는 FAISS(Facebook AI Similarity Search)가 가장 많이 쓰이는 벡터 검색 솔루션이었다. 
두 방식의 차이를 이해하면 어떤 상황에서 무엇을 선택할지 명확해진다.

 

FAISS + S3

FAISS는 벡터를 파일(`.faiss`, `.pkl`)로 저장하고, 검색 시 파일 전체를 메모리에 올려서 탐색한다.

 

흐름:
텍스트 → 임베딩 → FAISS 인덱스 파일 → S3 업로드

검색:
S3에서 파일 전체 다운로드 → 메모리 로드 → 유사도 계산

 

장점 단점
설치/관리 불필요 (파일만 있으면 됨) 데이터가 늘어나면 매 검색마다 대용량 파일을 메모리에 로드
소규모 데이터에서 검색 빠름 파일 기반이라 동시 쓰기 불가 (경쟁 조건 발생)
S3에 저장하므로 별도 DB 비용 없음 기존 RDB 테이블과 JOIN 불가
별도 인프라 구축 소요 시간 X 벡터 단건 삭제/수정이 어렵고 전체 재생성 필요

 

 

pgvector + PostgreSQL

pgvector는 벡터를 일반 DB 컬럼처럼 저장하고, SQL로 검색한다.

흐름:
텍스트 → 임베딩 → PostgreSQL INSERT

검색:
SQL SELECT + 벡터 연산자 → 결과 반환



장점 단점
기존 테이블과 SQL JOIN 가능 PostgreSQL 서버 필요
단건 INSERT/UPDATE/DELETE 가능 초대용량(수천만 건 이상)에서는 전용 벡터 DB보다 느릴 수 있음
동시 쓰기 지원 (ACID 트랜잭션) 쿼리 튜닝, 인덱싱 등 성능을 위한 작업 필요
데이터 증가에도 인덱스로 성능 유지 가능  

 

 

이러한 장단점이 있으니, 데이터의 규모, 고려해야하는 상황 등을 파악하여 적절히 선택하여 사용하면 좋을 것 같다.

 


 

3. 설치 방법

 

AWS RDS PostgreSQL 15+

RDS에는 pgvector가 이미 탑재되어 있다. 활성화만 하면 된다.

-- 해당 데이터베이스에 접속 후 실행 (최초 1회)
CREATE EXTENSION IF NOT EXISTS vector;

-- 설치 확인
SELECT * FROM pg_extension WHERE extname = 'vector';

 

* 권한 오류가 날 경우 RDS 마스터 계정으로 실행하거나, DBA에게 별도 생성 요청을 해야할 수 있다

* AWS 콘솔에서 파라미터 그룹의 `shared_preload_libraries`에 `vector`를 추가 후 재시작이 필요할 수 있다.

 

 

로컬 PostgreSQL

1) Docker

docker run -d \
--name pgvector \
-e POSTGRES_PASSWORD=password \
-p 5432:5432 \
pgvector/pgvector:pg16


2) 직접 설치 (bash)
# macOS
brew install pgvector


# Ubuntu
sudo apt install postgresql-16-pgvector

 

 

 


 

 

 4. 테이블 설계

기본 구조

pgvector 테이블은 일반 테이블에 `vector(차원수)` 컬럼만 추가한 형태다.

 

CREATE TABLE items (
    id     SERIAL PRIMARY KEY,
    content    TEXT,
    embedding  vector(1536)  -- Titan v1 기준 1536차원
);

 

 

FK 설정 여부

FK를 걸면 참조 무결성이 보장되지만, 배치 임베딩 시 원본 테이블에 없는 id가 하나라도 있으면 전체 실패한다. 임베딩 테이블은 대량 INSERT가 잦으므로 FK 없이 애플리케이션 레벨에서 관리하는 방식을 많이 택한다.

-- FK 있는 버전 (무결성 보장, 배치 삽입 시 주의)
item_id INT REFERENCES items(id)

-- FK 없는 버전 (유연, 대량 삽입에 유리)
item_id INT

 

 

스키마 설계 패턴

원본 테이블과 임베딩 테이블을 1:1로 매핑하는 패턴이 일반적이다.



검색 시 JOIN으로 원본 데이터와 함께 조회한다.

SELECT i.id, i.title,
       e.embedding <-> '[...]'::vector AS distance
FROM item_embeddings e
JOIN items i ON e.item_id = i.id
ORDER BY distance
LIMIT 10;

 

 


 

 

5. 거리 계산 방식 3가지

pgvector는 세 가지 거리 연산자를 제공한다. 같은 임베딩이라도 어떤 연산자를 쓰느냐에 따라 검색 순위가 달라질 수 있다.

 

 

<-> L2 거리 (유클리드 거리)

  • 두 벡터를 공간상의 두 점으로 보고, 직선 거리를 잰다.
  • 값이 작을수록 가깝다.
SELECT *, embedding <-> query_vector AS distance
FROM items ORDER BY distance LIMIT 5;

 

FAISS의 기본 방식이 L2라서, FAISS와 동일한 결과를 원하면 이걸 쓰면 된다.

 

 

<=> 코사인 거리

  • 두 벡터가 얼마나 같은 방향을 가리키는지로 유사도를 계산한다.
  • 크기는 무시하고 방향만 본다.
SELECT *, embedding <=> query_vector AS distance
FROM items ORDER BY distance LIMIT 5;

 

값이 0에 가까울수록 유사, 2에 가까울수록 반대 의미다.

 

 

 

<#> 내적 (Negative Inner Product)

  • 두 벡터의 내적을 계산한다. 결과가 음수로 나오므로 값이 작을수록(더 음수일수록) 유사하다.
  • 벡터가 정규화되어 있으면 코사인과 동일한 결과를 내고, 셋 중 연산이 가장 빠르다.
SELECT *, embedding <#> query_vector AS distance
FROM items ORDER BY distance LIMIT 5;

 

 

비교

연산자 방식 크기 반영 범위
<-> L2 (유클리드) O 0 ~ ∞
<=> 코사인   0 ~ 2
<#> 내적 (음수) O -∞ ~ 0

 

 


 

6. Python 연동 코드 (psycopg2)

 

연결 및 기본 쿼리

import os
import psycopg2
import psycopg2.extras

conn = psycopg2.connect(
  host=os.getenv("POSTGRESQL_HOST"),
  port=int(os.getenv("POSTGRESQL_PORT")),
  user=os.getenv("POSTGRESQL_USER"),
  password=os.getenv("POSTGRESQL_PASSWORD"),
  dbname=os.getenv("POSTGRESQL_DATABASE"),
)

 

 

벡터 INSERT

Python 리스트를 그대로 전달하면 psycopg2가 PostgreSQL 배열로 변환한다. 

`vector` 타입은 배열로부터 암묵적 변환을 지원하므로 별도 처리 없이 동작한다.

from langchain_aws import BedrockEmbeddings

model = BedrockEmbeddings(model_id="...", region_name="...")
vector = model.embed_documents(["검색할 텍스트"])[0]  # list[float]

with conn.cursor() as cur:
    cur.execute(
        "INSERT INTO item_embeddings (item_id, embedding) VALUES (%s, %s)",
        (1, vector)  # vector는 list[float] 그대로 전달
    )
    conn.commit()

 

벡터 검색

query_vector = model.embed_query("검색어")

with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
    cur.execute(
        """
        SELECT e.item_id, e.title,
               t.embedding <-> %s::vector AS distance
        FROM item_embeddings t
        JOIN items e ON t.item_id = e.id
        ORDER BY distance
        LIMIT %s
        """,
        (query_vector, 5)
    )
    results = cur.fetchall()

for row in results:
    print(f"거리={row['distance']:.4f} | {row['title']}")

 

 


 

 

7. JOIN 검색 실전 예시

pgvector의 가장 큰 장점 중 하나는 기존 테이블과 SQL JOIN이 가능하다는 것이다.

 

 

기본 JOIN 검색

SELECT
    e.id,
    e.title,
    e.category,
    e.status,
    t.embedding <=> '[...]'::vector AS distance
FROM item_embeddings t
JOIN items e ON t.item_id = e.id
WHERE e.status = 'active'
ORDER BY distance
LIMIT 10;

 


조건 필터링 + 벡터 검색

-- 특정 카테고리 안에서 유사도 검색
SELECT e.title, t.embedding <-> query AS distance
FROM item_embeddings t
JOIN items e ON t.item_id = e.id
WHERE e.category = 'tutorial'
ORDER BY distance
LIMIT 5;
유사도 임계값 설정

-- 거리가 0.5 이하인 결과만 반환 (너무 동떨어진 결과 제외)
SELECT e.title, t.embedding <-> query AS distance
FROM item_embeddings t
JOIN items e ON t.item_id = e.id
WHERE t.embedding <-> query < 0.5
ORDER BY distance
LIMIT 10;


이처럼 pgvector는 벡터 검색과 일반 SQL 조건을 자유롭게 조합할 수 있어,
기존 RDB 기반 서비스에 AI 검색 기능을 붙이는 데 매우 적합하다.

 

 

728x90
저작자표시 (새창열림)

'Python' 카테고리의 다른 글

벡터 임베딩이란  (0) 2026.04.17
Video Generation Pipeline 구조 분리 기록  (0) 2026.03.22
LongCat Avatar: num_segments 자동 계산 로직 추가  (0) 2026.03.19
pthread_setaffinity_np failed for thread ... Invalid argument. Specify the number of threads explicitly so the affinity is not set.  (0) 2026.03.18
Fish-speech tts 테스트 및 문제 해결(3)  (0) 2026.03.16
'Python' 카테고리의 다른 글
  • 벡터 임베딩이란
  • Video Generation Pipeline 구조 분리 기록
  • LongCat Avatar: num_segments 자동 계산 로직 추가
  • pthread_setaffinity_np failed for thread ... Invalid argument. Specify the number of threads explicitly so the affinity is not set.
에이전트공방
에이전트공방
Agent로 먹고살기..🛠️
  • 에이전트공방
    에이전트공방
    에이전트공방
  • 전체
    오늘
    어제
    • 분류 전체보기 (109)
      • Python (19)
      • AWS (5)
      • Server (7)
      • Docker (7)
      • GIT (7)
      • Node (9)
      • React (11)
      • OS (8)
      • Java (3)
      • JavaScript (11)
      • Vue (12)
      • CSS (5)
  • 링크

  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
에이전트공방
PostgreSQL과 PgVector에 관하여
상단으로

티스토리툴바