RAG나 의미 검색을 만든다고 해서 항상 벡터 데이터베이스가 필요한 것은 아니다. 코퍼스가 100만 문서 안팎이고, 임베딩을 한 번에 만들어 메모리에 올릴 수 있으며, 쓰기보다 읽기가 많은 낮은 트래픽 서비스라면 numpy 행렬곱 기반 브루트포스 검색이 운영 복잡도를 크게 줄인다.
핵심 아이디어
scores = doc_vectors @ query_vector.astype(np.float32, copy=False)384차원 float32 임베딩을 행렬로 들고 있다가 쿼리 벡터와 내적한다. SoftwareDoug의 M4 MacBook Pro 실험에서는 100만 벡터에서 단일 클라이언트 79.7 QPS, 평균 12ms가 나왔다. 884만 벡터에서도 단일 클라이언트 9.34 QPS, 평균 106ms 수준이었다.
이 방식이 맞는 조건
- 문서 수가 수십만~수백만 단위다.
- 전체 임베딩 행렬이 메모리에 들어간다.
- 인덱스 업데이트가 실시간으로 자주 일어나지 않는다.
- 복잡한 필터, 멀티테넌시, 복제, 백업, 대규모 동시성이 아직 필요 없다.
- 검색 품질 실험이 먼저이고 인프라 운영은 나중 문제다.
벡터 DB로 넘어갈 신호
브루트포스가 느려져서 SLA를 못 맞추거나, 메타데이터 필터링과 하이브리드 검색이 필요하거나, 인덱스를 여러 서버에 나눠야 한다면 vector-db-tips-comparison의 Qdrant, Weaviate, Milvus, pgvector 같은 선택지로 넘어간다. 핵심은 “처음부터 벡터 DB를 사는 것”이 아니라, 단순한 exhaustive search가 실제 병목이 되는 시점을 측정하는 것이다.
참고 자료
- Just brute force your embeddings — SoftwareDoug (2026-07-29)