
Postgres만으로 벡터 검색 성능 28배 올리기: pgvectorscale 도입기
PostgreSQL과 pgvector 환경에서 발생하는 메모리 부족 및 비용 문제를 해결하고, pgvectorscale의 StreamingDiskANN을 통해 벡터 검색 성능을 28배 올리는 방법을 소개합니다.
김영태
테크리드

안녕하세요. 풀링포레스트 테크리드 김영태입니다.
요즘 개발팀 회의실에서 가장 많이 들리는 단어는 단연 'RAG(검색 증강 생성)'와 '임베딩'일 겁니다. 저희 팀도 예외는 아니었습니다. 서비스에 AI 기능을 붙이면서 수백만 건의 벡터 데이터를 다루게 되었는데, 처음엔 익숙한 PostgreSQL에 `pgvector` 확장만 설치해서 가볍게 시작했습니다. "Postgres 하나로 다 되네? 역시 만능이야"라며 좋아했던 것도 잠시, 데이터가 쌓이고 트래픽이 몰리자 모니터링 대시보드의 레이턴시 그래프가 춤을 추기 시작하더군요.
솔직히 말해, 꽤 막막했습니다. 메모리 사용량은 치솟고, 쿼리 속도는 느려지고... 그렇다고 이제 와서 Pinecone이나 Milvus 같은 전용 벡터 DB를 따로 구축하자니 인프라 복잡도가 올라가는 게 부담스러웠습니다. 기존 RDB와의 조인(Join) 쿼리도 포기하기 힘들었고요. 운영팀에서는 "RDS 비용이 너무 많이 나오는데요?"라며 압박을 주는데, 정말 등에서 식은땀이 흘렀습니다.
그러다 발견한 것이 바로 오늘 소개할 `pgvectorscale`입니다. Timescale에서 내놓은 이 확장은 기존 `pgvector`를 대체하는 게 아니라, 보완해서 성능을 비약적으로 끌어올려주는 물건입니다. 오늘은 제가 이 도구를 검토하며 알게 된 내용과, 왜 이것이 우리 같은 백엔드 개발자들에게 구원투수가 될 수 있는지 정리해보려 합니다.
왜 pgvector만으로는 부족했을까?
기본적으로 `pgvector`는 HNSW(Hierarchical Navigable Small World) 알고리즘을 주로 사용합니다. HNSW는 빠르지만 치명적인 단점이 하나 있습니다. 바로 인덱스를 전부 메모리에 올려야 성능이 나온다는 점입니다. 데이터가 5천만 건, 1억 건으로 늘어나면 그만큼 RAM을 증설해야 하는데, 클라우드 비용 청구서를 받아보신 분들은 아실 겁니다. 메모리 빵빵한 인스턴스가 얼마나 비싼지요.
저희가 마주한 '벽'도 바로 여기였습니다. 데이터는 디스크에 넘쳐나는데, 메모리가 부족해서 허덕이는 상황. 이때 `pgvectorscale`이 제시한 해결책은 꽤나 충격적이었습니다.
StreamingDiskANN: 메모리 밖으로 눈을 돌리다
`pgvectorscale`의 핵심 무기는 `StreamingDiskANN`이라는 새로운 인덱스 타입입니다. Microsoft의 DiskANN 알고리즘에서 영감을 받은 기술인데, 쉽게 말해 "인덱스를 굳이 다 메모리에 올리지 말고, 빠른 NVMe SSD에 두고 필요할 때만 스트리밍하자"는 접근입니다.
이게 왜 대단하냐면, 비싼 RAM 대신 상대적으로 저렴한 SSD를 활용해서 대용량 벡터를 처리할 수 있게 해주기 때문입니다. 덕분에 비용 효율이 엄청나게 좋아집니다. 벤치마크 결과를 보면 Pinecone의 스토리지 최적화 인덱스(s1) 대비 성능(p95 latency)은 28배 빠르고, 처리량은 16배 높으면서, 비용은 75%나 절감된다고 합니다.
물론 벤치마크는 벤치마크일 뿐이라 실제 워크로드에선 다를 수 있겠지만, "비용 절감"과 "성능 향상"을 동시에 잡을 수 있다는 구조 자체가 엔지니어로서 매우 흥미로웠습니다.

더 똑똑한 압축: SBQ (Statistical Binary Quantization)
또 하나 인상 깊었던 건 압축 기술입니다. 보통 벡터 사이즈를 줄이려고 Binary Quantization(BQ)을 많이 쓰는데, 이러면 정확도(Recall)가 떨어지는 경우가 많습니다. `pgvectorscale`은 여기서 한 발 더 나아가 `Statistical Binary Quantization`이라는 기법을 씁니다. 데이터의 분포를 통계적으로 분석해서 압축하기 때문에, 표준 BQ보다 훨씬 더 높은 정확도를 유지하면서도 용량을 줄여줍니다. 개발자 입장에서는 "용량은 줄이고 싶은데 검색 품질 떨어지는 건 싫어"라는 딜레마를 해결해 주는 셈입니다.
실전: 어떻게 적용할까?
이 확장은 특이하게도 Rust로 작성되었습니다. PGRX 프레임워크를 사용했는데, C로 짜인 기존 확장들보다 안정성 면에서 신뢰가 갔습니다. 설치도 생각보다 간단합니다. Docker를 쓴다면 이미지가 제공되고, 소스 빌드도 가능합니다.
가장 빠른 테스트 방법은 Docker를 이용하는 겁니다.
# TimescaleDB 도커 실행
docker run -d --name pgvectorscale -p 5432:5432 -e POSTGRES_PASSWORD=password timescale/timescaledb:latest-pg16
# DB 접속 후 확장 설치
psql -d "postgres://postgres:password@localhost:5432/postgres"SQL 콘솔에서 다음 명령 한 줄이면 설치가 끝납니다. `CASCADE` 옵션을 주면 `pgvector`가 없어도 알아서 같이 설치해줍니다.
CREATE EXTENSION IF NOT EXISTS vectorscale CASCADE;단, 주의할 점이 하나 있습니다. 문서를 보니 현재 macOS Intel(x86) 머신에서는 빌드 이슈가 있다고 합니다. 저처럼 M1/M2 맥북을 쓰시거나 리눅스 환경이라면 문제없지만, 혹시라도 구형 맥을 쓰시는 분들은 Docker를 활용하시는 게 정신 건강에 이롭습니다.
메타데이터 필터링의 고통 해소
RAG를 구현하다 보면 단순히 "비슷한 거 찾아줘"가 아니라 "최근 1주일 내 문서 중에서, 작성자가 '김영태'인 것 중에 비슷한 거 찾아줘" 같은 요구사항이 반드시 들어옵니다. 기존 벡터 검색에선 이런 메타데이터 필터링(Post-filtering)이 들어가면 성능이 곤두박질치곤 했습니다.
하지만 `pgvectorscale`은 마이크로소프트의 Filtered DiskANN 연구를 기반으로 라벨 기반 필터링(Label-based filtered vector search) 기능을 내장하고 있습니다. 덕분에 `WHERE` 절이 복잡하게 붙어도 벡터 검색 성능이 크게 저하되지 않습니다. 저희 팀이 가장 골머리를 앓던 부분이 바로 이 필터링 성능이었기에, 이 기능이 특히 반가웠습니다.
마무리하며
기술은 돌고 돈다고 하죠. NoSQL이 세상을 지배할 것 같았지만, 결국 다시 SQL과 RDB의 강력함으로 회귀하는 흐름을 종종 봅니다. 벡터 DB 시장도 마찬가지인 것 같습니다. 전용 DB들이 화려하게 등장했지만, 결국 개발자들에게 가장 편한 건 "이미 쓰고 있는 Postgres에서 잘 돌아가는 것" 아닐까요?
`pgvectorscale`은 Postgres를 단순한 데이터 저장소가 아니라, 고성능 AI 애플리케이션의 핵심 엔진으로 업그레이드해 줄 수 있는 강력한 도구입니다. 지금 RAG 성능이나 비용 때문에 고민하고 계신다면, 인프라를 갈아엎기 전에 이 확장을 먼저 테스트해 보시길 강력하게 추천합니다.
저희 풀링포레스트 팀도 이제 막 도입 테스트를 마쳤습니다. 앞으로 운영 환경에서 겪게 될 트러블슈팅 경험들도 차차 블로그를 통해 공유하겠습니다. 오늘도 트래픽과 싸우는 모든 개발자분들, 화이팅입니다.


