안녕하세요! 이번 포스팅은 오랜만에 데이터베이스와 관련된 포스팅이네요. 이번엔 제가 주로 공부하는 RDBMS가 아닌 벡터 데이터베이스에 대해서 공부해보고 정리해보는 시간을 가져볼까합니다.
sLLM을 이용해서 개발하고있지만 벡터 데이터베이스를 깊이있게 사용하지 않아서 잠시 옆으로 두고 있었는데 이제 조만간 대학원을 가야하는데 졸업 논문을 편하게 쓰려면 1학기부터 논문 주제를 슬슬 정하는게 좋겠다고 생각해서 이런저런 주제를 생각하다가 벡터 데이터베이스와 관련된 내용을 쓰면 좋겠다싶어 미리 공부할겸 제반지식을 쌓기 위해 공부하고 정리하려고 합니다.
이번 포스팅에서는 다음과 같은 순서로 진행됩니다.
- RDBMS에서의 인덱스 : 이 챕터에선 기존 RDBMS가 채택했던 인덱스 알고리즘인 B+Tree에 대해서 간단하게 리뷰해보는 시간을 가져볼까합니다.
- 벡터 데이터베이스에서의 인덱스 : 해당 챕터에서는 RDBMS에 B+Tree가 있다면 벡터 데이터베이스에는 HNSW가 있습니다. 이 HNSW가 무엇이고 어떤 점에서 RDBMS와 차별점이 있는지 알아봅니다.
- HNSW의 동작 방식 : 앞에서 다뤘던 HNSW에서 조금 더 깊이있게 탐구해보는 시간을 가질건데 여기서는 어떻게 벡터 데이터베이스가 목적지까지 이동하는지에 대한 흐름으로 이야기를 풀어볼까합니다.
- 벡터 데이터베이스의 종류와 각각의 특징들 : RDBMS도 Oracle, MySQL, MariaDB, PostgreSQL, MSSQL, SQLite 이렇게 여러개인 것과 마찬가지로 대표적으로 Pinecone, Milvus, Qdrant, Chroma, Weaviate 등이 있습니다. 각각의 벡터 데이터베이스들의 특징을 살펴보고 어떤 점에서 강점을 가지며 실무에서는 어떤 기준으로 데이터베이스를 선택해야하는지 장점과 단점의 트레이드오프 관점에서 이야기를 풀어봅니다.
- Pre-filtering vs Post-filtering : 필터링 작업은 RDBMS에서 WHERE절을 이용해서 필터링하듯이 걸러내는 작업을 할 수 있는데 검색 전에 필터링하냐 검색 후에 필터링을 하느냐에 따라서 추구하는 목표도 다르고 결과물도 완전 달라지게 되는데요 이 부분을 조금 집중적으로 다룰 예정입니다.
- 하이브리드 검색과 RRF : 전통적인 키워드 검색 방식과 벡터 기반 검색은 각각이 잘하는 분야가 완전히 다른데요. 키워드 검색과 벡터 기반 검색을 합쳤을 때의 강력함이 묻어나오는데 암호화 알고리즘인 Argon2i와 Argon2d를 합쳐 장점은 돋보이게 하고 단점을 희석시키는 Argon2id와 같은 방식으로 서로의 장점만 쏙 빼먹은 하이브리드 검색에 대해서 알아봅니다.
- Product Quantization (PQ) : 벡터 데이터베이스의 단점 중 하나는 노드들의 의미적인 연결을 메모리에 다 올리고 있어야 한다는 것인데 간단한 꼼수를 활용해 메모리를 최소 4배 최대 64배까지 최적화할 수 있는 방법론인 PQ에 대해서 알아봅니다.
- 실무 관점에서의 벡터 데이터베이스 튜닝 : 실무에서는 어떤 부분을 어떻게 조정해야 우리 서비스에 핏하게 맞는 벡터 데이터베이스를 만들 수 있을지를 고민해봅니다.
오랜만에 볼륨이 큰 포스팅으로 찾아뵙게 되었네요. 조금 무겁지만 최대한 단순하게 정리해보겠습니다.
RDBMS에서의 인덱스
RDBMS에서는 크기 기반 인덱싱을 주로 사용하는데요. 주로 사용되는 B+Tree의 경우 숫자 혹은 알파벳의 정렬 순섣재로 이진 트리 형식위에 노드를 위치시킵니다. 또한, B+Tree의 특징 덕분에 Depth가 3이상으로 가는 일이 거의 없어 RDBMS는 이 구조 덕분에 읽기 성능에 대한 성능을 보장받을 수 있게 되었습니다.
다만, 이 때문에 RDBMS에서 읽기를 할 때는 완전히 키워드가 일치하지 않으면 검색할 수 없다는 단점이 생겼는데요. WHERE절에서 정확히 맞는 문자열을 검색해야하고 이보다 조금 나은 LIKE를 사용하더라도 "의미"기반의 검색은 할 수 없게 되었습니다.
또한, 이런 인덱스 구조 때문에 쓰기 연산에서 큰 단점이 생기게 되었습니다. 쓰기 연산을 하게 되면 데이터가 이진 트리 사이를 비집고 들어와야 했기에 노드를 찢거나 다시 합치는 등의 추가 연산이 필요해 인덱스를 재정렬할 시간이 있어야했고 이는 자연스럽게 쓰기 연산에 대한 부하로 이어졌습니다.
뿐만 아니라, 일관성을 보장받아야 했기에 MVCC를 위해 데이터간 락을 걸어 쓰기 연산에 더 큰 부하가 생긴 것은 덤이었죠.
이런 결과로 읽기 연산이 압도적으로 많은 서비스나 강한 일관성이 보장받아야하는 서비스에서 RDBMS를 사용하게 되었고 쓰기 연산이 많은 SNS같은 서비스에서는 자연스럽게 NoSQL을 채택하게 되었습니다.
벡터 데이터베이스에서의 인덱스
기존에도 벡터 데이터베이스에 대한 연구와 개발이 이루어졌지만 웬만한 서비스는 RDBMS나 NoSQL로 커버가 되었기 때문에 벡터 데이터베이스는 그대로 잊혀지는 듯 했습니다. 하지만 최근 LLM의 급부상과 RAG의 도입으로 인해 벡터 데이터베이스는 날개를 달았는데요.
확률적으로 다음 단어를 생성하는 Transformer의 특성상 할루시네이션이라는 엄청난 단점이 생기게 되었고 개발자들은 이를 해결하기 위한 방법으로 벡터 데이터베이스를 선택하게 되었습니다.
기존 RDBMS와 NoSQL은 정확히 일치하는 검색만 가능했지만 LLM 서비스 특성상 사용자가 애매모호한 질의를 심지어 자연어로 날리는 경우가 많아 의미기반의 검색이 가능한 벡터 데이터베이스가 훌륭한 선택지가 되었던 것이죠. 또한, 벡터 데이터베이스를 도입한 것만으로도 LLM에게 정답지를 쥐어주는 효과가 발생해 할루시네이션을 낮출 수 있게 되었습니다.
그럼 벡터 데이터베이스는 어떻게 의미 기반의 검색이 가능했던 것일까요?
바로 HNSW (Hierarchical Network Small World) 덕분입니다.
HNSW 란?
HNSW는 NSW라는 알고리즘에서 출발합니다. NSW는 클러스터링 된 군집에서 각각의 오브젝트들과 선으로 연결해 그래프로 표현하는 알고리즘인데요. 이는 마치 Graph DB와 비슷한 맥락인데 Graph DB와 다른 점은 노드간 관계에 집중한 Graph DB와 다르게 벡터 데이터베이스는 각 노드간 물리적 근접도만을 위한 점이 가장 크게 다른 점입니다.

이는 NSW가 생성되는 과정을 보여주는 그림인데 점이 하나 찍힐 때마다 2개씩 인접 노드와 선으로 연결되는 것을 보실 수 있습니다. 그것이 NSW의 핵심이고 이 군집을 이용해서 가까운 거리를 찾아내는 것이 기본 골자입니다.

이렇게 연결된 노드들은 실제 질의와 관련된 노드와 가장 가까운 노드를 선별해서 검색할 수 있게 되었는데 시작점에서 들어와 가장 가까운 노드로 이동하는 Greedy Search를 기반으로 질문과 가까운 노드를 찾을 수 있습니다.
가까운 거리를 구하는 것은 일반적으로 하나의 노드에 연결된 모든 노드의 거리를 검색하는 것이 일반적이지만 그렇게 하면 노드의 개수가 조금만 늘어나도 바로 문제가 되기 때문에 이보다는 단계적인 레이어 계층을 두어 거리를 최소화하는 방법을 선택하는 방향으로 진화하게 되었습니다. 그것이 바로 여기에 수직 계층을 추가한 HNSW이죠.
HNSW의 동작 방식
HNSW는 스무고개와 굉장히 비슷합니다. 우선 큰 틀에서부터 점점 작은 틀로 좁혀나가 결국 원하는 목적지에 도달하는 것인점이 비슷한데 예를 들어서 "L2캐시"라고 질문이 온다면 HNSW 인덱스는 "가전제품", "PC", "CPU", "L2캐시" 이렇게 단계적으로 목적지에 찾아가는 것이죠.
벡터 데이터베이스는 클러스터링을 할 때 다음과 같은 동작 방식을 따릅니다.
- 레이어 결정 : 새로운 벡터가 들어오면 이 노드가 어느 층까지 올라갈지를 먼저 결정합니다. 이는 지수 확률 분포를 따르기 때문에 대부분의 노드는 바닥 레이어에 머물지만 아주 작은 활률로 상위 레이어까지 배치됩니다.
- 진입 및 탐색 : 레이어가 결정되면 최상위 레이어의 진입점부터 시작해서 자신이 배치될 레이어까지 Greedy Search로 내려옵니다. 여기서 Greedy Search는 현재 노드와 연결된 모든 이웃 노드들의 좌표를 가져와 이웃 누드간 거리를 코사인 유사도로 계산한 뒤 만약 이웃 중 하나라도 질문과 가깝다면 미련 없이 그 노드로 자리를 옮겨버립니다. 만약 모든 이웃을 다 뒤져봤는데 나보다 더 가까운 노드가 없다면 그 지점에서 탐색을 멈춥니다.
- 이웃 선택과 연결 : HNSW에서는 단순히 가장 가까운 M(최대 연결 수)개의 노드와 연결하는 것이 아닙니다. 벡터 데이터베이스는 다양성을 고려한 휴리스틱 알고리즘을 사용하는데, 가장 가까운 놈들끼리만 연결이 되면 특정 영역에서 노드들이 뭉쳐버리는 "클러스터 현상" 이 발생하기 때문입니다. 그렇기 때문에 일단 가장 가까운 노드를 연결하고 그 다음 노드를 연결할 때는 이미 연결된 노드보다 새로운 노드가 나와 가까운가를 체크합니다. 만약 이미 연결된 노드와 너무 가까운 방향에 있는 노드라면 버리고 조금 더 멀더라도 새로운 방향에 있는 노드를 선택해서 연결합니다. 이를 통해 고차원 공간 구석구석으로 뻗어 나가는 사방팔방의 인맥이 형성됩니다.
- 양방향 연결 및 가지치기 : 새 노드가 기존 노드에 선을 연결하면 기존 노드 입장에서는 연결된 선이 M을 초과할 수 있는데 이 경우 기존 노드도 다시 휴리스틱 알고리즘을 돌려 가장 효율적인 M개의 선만 남기고 나머지는 끊어버리는 가지치기를 수행합니다.
이런 과정을 거치면 HNSW가 동작하게 되는데 HNSW가 이런 동작 방식을 가지게 되면서 Greedy Search에서 생기는 단점이 발생하지 않게 되었습니다. Greedy Search는 가장 가까운 노드만 돌다가 결국 한 군데에서 빙빙 도는 현상이 생기게 되는데 HNSW는 이미 상위 레이어에서 질문과 가까운 답변이 있는 곳에 위치하기 때문에 Greedy Search의 단점이 발생하지 않게 되는 것이죠.
벡터 데이터베이스의 종류와 특징
기본적으로 벡터 데이터베이스들은 HNSW를 채택하고 있습니다. 다만 조금씩 성격이 다른데 한번 정리해보겠습니다.
- Pinecone : Pinecone은 인프라 설정이 전혀 필요없는 완전 관리형 SaaS입니다. 초기 구축 속도가 압도적으로 빠르며 서비스 규모에 따라 알아서 스케일링이 된다는 장점이 있습니다. 다만 추가 비용이 생기고 블랙박스 구조로 비용 예측이 어렵다는 단점이 있습니다.
- Milvus : 벡터 데이터베이스계의 쿠버네티스이고 이런 이명답게 가장 복잡하지만 가장 강력한 확장성을 가집니다. 데이터를 저장하는 노드, 쿼리를 실행하는 노드, 인덱싱을 하는 노드가 모두 분리된 분산 시스템이기 때문에 분산 처리에 특화되어있고 수평 확장에 매우 능합니다. Milvus는 엔터프라이즈급 시스템에 가장 잘 어울리는 벡터 데이터베이스입니다. 다만 아키텍처가 매우 복잡해 관리 포인트가 한두개가 아니라는 문제가 있고 쿠버네티스에 대한 이해도가 없으면 잘 활용하기 힘든 데이터베이스입니다.
- Qdrant : Rust로 작성된 데이터베이스여서 메모리 효율과 연산 속도가 매우 빠르다는 특징이 있습니다. Rust 특유의 메모리 안전성과 속도 덕분에 동일한 사야에서 높은 처리량을 보여줍니다. API가 직관적이고 성능 튜닝 옵션이 세밀하다는 특징이 있습니다만 Rust로 작성되어 커뮤니티가 상대적으로 작아 성숙하지 못하다는 단점이 있습니다.
- Chroma : Python기반의 매우 단순한 인터페이스를 제공하고 내부적으로는 SQLite를 사용한다는 특징이 있습니다. 복잡한 설정 없이 바로 RAG 환경을 구축할 수 있어 빠른 검증용으로 유용합니다만 조금만 규모가 있는 서비스에서는 사용할 수 없다는 단점이 있습니다.
- Weaviate : Go로 작성되어 성능이 준수하고 GraphQL 지원 등 백엔드 개발자가 선호하는 인터페이스를 갖추고 있습니다. 또한, 하이브리드 검색이 가능하고 백엔드 친화적이지만 메모리 점유율이 높아 PQ를 적용해야하는 경우가 많습니다. 왜 Weaviate가 메모리를 많이 점유하게 되었는지는 추후에 서술합니다.
Pre-filtering vs Post-filtering
벡터 검색도 RDBMS의 WHERE절처럼 필터링을 진행할 수 있는데 이 필터링을 검색 전에 하느냐 검색 후에 하느냐에 따라 추구하는 방향도 다르고 검색 결과도 완전히 다르게 되는데요. 한번 자세히 알아보겠습니다.
Pre-filtering
Pre-filtering은 벡터 검색을 수행하기 전에 메타데이터 조건을 만족하는 데이터들만 먼저 골라내는 방식입니다.
- 동작 방식 : 먼저 SQL의 WHERE절처럼 조건을 만족하는 문서들의 ID를 필터링한 후, 그 결과 집합 안에서 유사도 검색을 수행합니다.
- 장점 : 필터링된 결과 내에서 가장 유사한 K개를 반드시 찾습니다. 필터 조건에 맞는 데이터가 존재하기만한다면 결과가 누락될 일이 없습니다.
- 단점 : Pre-filtering의 가장 큰 단점은 HNSW 인덱스가 무력화될 위험이 있다는 것입니다. HNSW처럼 그래프 기반의 인덱스에서는 그래프의 노드 중 상당수가 필터링으로 삭제된 것처럼 처리되면, 검색 경로가 끊겨버려 성능이 급격히 떨어지거나 검색이 실패할 수도 있습니다. 또한, 필터링된 결과가 너무 많으면 전체를 스캔해야하므로 속도가 느려진다는 단점도 있습니다.
Post-filtering
벡터 검색을 먼저 수행하여 상위 K개를 뽑고 그 결과에 메타데이터 필터를 적용하는 방식입니다.
- 동작 방식 : 전체 데이터셋에서 유사도가 높은 K개를 먼저 가져온 후, 그 중 특정 조건에 맞는 데이터를 버립니다.
- 장점 : 기존의 벡터 인덱스를 그대로 활용하므로 매우 빠르면서도 인덱스 구조를 건드리지 않아도 된다는 장점이 있습니다.
- 단점 : 예를 들어서 100개를 뽑았는데 필터 조건에 맞는 데이터가 2개뿐이라면, 사용자는 100개를 요청했더라도 2개만 받게 됩니다. 최악의 경우 검색 결과가 0개가 될 수도 있습니다.
실무적 의사결정
HNSW 그래프가 Pre-filtering 시 끊기는 문제는 벡터 데이터베이스의 성능과 정확도를 갉아먹는 고질적인 병목 중 하나입니다.
문제의 핵심은 Pre-filtering으로 벡터 검색을 시작하기 전에 메타데이터 조건에 맞지 않는 노드들을 검색 대상에서 제거하기 때문에 만약 필터링 조건이 매우 까다로워서 데이터의 90퍼센트가 제거된다면, 그래프의 연결 고리들이 대거 사라집니다. 노드간 연결로 목적지를 찾아가는 HNSW에서는 치명적이죠. Pre-filtering의 결과 그래프가 여러개의 섬으로 쪼개지고 검색 시작점에 있는 섬에서 목적지가 있는 섬으로 가는 길이 끊어지면 목적지가 분명 존재하는데도 불구하고 검색 엔진은 "결과 없음"을 반환합니다.
주요 벡터 데이터베이스들의 해결 방식
이 문제를 데이터베이스를 만드는 개발자들이 가만히 냅두지 않았겠죠? 어떻게 해결했을까요?
Milvus의 경우 그래프에서 노드를 물리적으로 제거하지 않고, 대신 모든 노드의 필터링 여부를 0과 1로 표시한 비트마스크를 메모리에 띄웁니다. 탐색시에는 필터링된 노드도 브릿지 역할로 활용하여 길을 찾고 최종 결과 리스트에 담을 때만 비트마스크를 확인해 필터링된 노드를 제거하는 방식입니다. 결과가 부족하면 탐색 범위를 넓혀가며 반복합니다.
Weaviate의 경우 ACRON-HNSW 알고리즘을 도입했는데 이 방식은 인덱스를 빌드할 때부터 필터링을 염두해두는 방식입니다. 일반적인 HNSW는 그래프를 가볍게 만들기 위해 불필요한 연결을 쳐내는데 반해 ACRON은 의도적으로 연결을 더 많이 남겨둡니다. 이로인해 필터링으로 많은 노드가 가려져도 우회로가 존재할 확률을 극대화합니다. 또한, 필터링 후 남은 데이터가 너무 적으면 아예 인덱스를 무시하고 Flat Search로 스위칭하여 100% 정확도를 보장합니다.
Qdrant는 필터 가능한 HNSW라는 독자적인 알고리즘을 사용합니다. 이 방식은 탐색 과정에서 "필터 조건에 맞는 노드"와 "그냥 길만 빌려주는 노드"를 구분합니다. 핵심은 HNSW의 탐색 알고리즘을 직접 수정해서 조건에 맞지 않는 노드라도 탐색 경로 상에 있다면 이를 통과할 수 있도록 허용해줍니다. 이를 통해 그래프의 단절 문제를 원천적으로 차단하죠.
하이브리드 검색
하이브리드 검색은 벡터 데이터베이스의 꽃이죠. 전통적인 키워드 검색(BM25A)과 최신 벡터 기반 검색은 서로 잘하는 분야가 완전히 다릅니다. BM25는 키워드 검색에 능해 "IPhone 15 Pro"와 같은 고유명사, 제품 코드, 약어를 찾을 때 압도적으로 좋습니다. 의미가 아니라 글자 그 자체의 일치 여부를 따지기 때문인데요.
반면 벡터 검색은 "어제 먹은 음식 때문에 배가 아플 때 먹는 약"과 같은 맥락과 의도를 파악하는데 아주 강합니다. "배가 아프다"와 "복통"의 유사성을 이해하기 때문이죠.
하이브리드 검색은 이 두개를 적절히 잘 섞어서 이 두개의 장점을 모두 흡수하는 방향으로 진화했습니다. 여기에서 가장 널리 사용되는 기법이 바로 RRF입니다.
RRF (Reciprocal Rank Fusion)
서로 다른 기준의 점수를 가진 두 리스트를 "순위"만을 이용해서 통합하는 알고리즘입니다. 점수 체계가 다른 두 엔진 (BM25 vs 코사인 유사도)을 공정하게 합칠 수 있다는 특징이 있습니다.
대부분의 벡터 데이터베이스들은 하이브리드 검색 시 알파라는 가중치 파라미터를 제공합니다. 이를 통해 어떤 엔진에 더 힘을 실어줄지 결정합니다.
- 알파 = 1 : 100% 순수 벡터 검색
- 알파 = 0 : 100% 순수 키워드 검색
- 알파 = 0.5 : 두 방식의 결과를 균등하게 반영
실무적 의사결정
실무에서는 보통 사용자의 쿼리가 짧고 명확한 단어 위주라면 알파 값을 낮추고, 서술형 문장이라면 알파 값을 높이는 식의 동적 튜닝을 고민해야합니다.
법률, 의료, 혹은 회사 내부 시스템처럼 특정 코드나 용어가 중요한 도메인의 경우에는 하이브리드 검색이 유용하고 임베딩 모델이 학습하지 못한 최신 유행어나 신제품명이 검색어인 경우에도 하이브리드 검색이 용이합니다.
Product Quantization (PQ)
벡터 데이터베이스의 고질적인 문제는 노드간의 연결들을 모두 메모리에 올려야한다는 문제가 있습니다. 이를 해결하기 위해 PQ라는 고차원 벡터를 여러개의 작은 부분 벡터로 쪼개고 각 부분 벡터를 대표값의 인덱스로 치환하여 압축하는 기술을 이용합니다.

예를 들어서 128차원 벡터를 8바이트로 압축하는 경우는 다음과 같습니다.
- 분할 : 128차원 벡터를 16차원짜리 부분 벡터 8개로 나눕니다.
- 학습 : 각 16차원 공간에서 K-Means 클러스터링을 수행해 대표적인 점 256개를 뽑습니다. 이 256개의 좌표 묶음을 코드북이라고 부릅니다.
- 부호화 : 이제 각 부분 벡터를 가장 가까운 대표값의 번호로 바꿉니다.
- 압축완료 : 원래 128개의 실수 (float32, 512바이트)였던 벡터가 8개의 인덱스 (8바이트)로 줄어드니 64배의 압축이 일어난겁니다.
PQ는 단순히 값을 뭉뚱그리는 일반적인 양자화와는 다르게 성능 손실이 최소화된다는 특징이 있는데 이유는 다음과 같습니다.
만약 128차원 전체를 한꺼번에 256개로 양자화했다면 표현할 수 있는 벡터의 가짓수는 딱 256개뿐입니다. 하지만 PQ로 8개 구역을 나누어 각각 256개씩 할당해준다면 표현 가능한 전체 조합수는 256의 8승개로 이는 10의 19승개나 됩니다. 이 원리가 부분으로 나누어 근사하더라도 전체 조합으로 정확도를 복원하는 PQ의 핵심 원리이죠.
PQ는 검색시에도 영리하게 동작하는데 사용자 질의는 압축하지 않고 원본 그대로를 유지한 채 DB에 저장된 압축된 벡터들과 거리를 계산합니다. 이 방식은 쿼리와 코드북 사이의 거리를 미리 계산해 테이블로 만든 후 실제 거리를 계산할 때에는 이 테이블에서 값만 읽어서 더한다는 특징이 있습니다. 이는 원본 쿼리를 보존하기 때문에 쿼리와 테이블을 모두 압축했을 때보다 오차가 훨씩 적습니다.
실무 관점에서의 벡터 데이터베이스 튜닝
그럼 실무에서는 어떻게 벡터 데이터베이스를 다뤄야할까요? 실무에서는 아래와 같은 조건을 보고 기술적 의사결정을 해야합니다.
- M 설정 : M이 클수록 그래프가 촘촘해져서 군집이 섬으로 나뉘는 현상이 줄어들고 검색 정확도가 올라가지만 인덱스 크기가 M에 비례해 커진다는 특징이 있습니다. M의 기본 값은 대부분 16이고 일반적인 텍스트 검색이나 RAG 시스템이라면 건드리지 않아도 됩니다. 하지만 임베딩 모델의 차원이 크거나 데이터 간의 경계가 모호하여 고도의 정밀도가 필요한 도메인에서는 32~64정도로 튜닝합니다. 만약 M을 2배 늘리면 차지하는 메모리도 2배 늘어나기 때문에 신중히 결정해야합니다.
- efConstruction : efConstruction은 인덱스를 생성할 때 얼마나 깊게 탐색하면서 연결 고리를 만들지 결정하는 값입니다. 이 값이 클수록 인덱스 구축 시간은 오래걸리지만, 그래프의 품질이 좋아져 Pre-filtering시 그래프가 끊기는 문제를 완화하기 위해 이 값을 높이기도 합니다. 보통 128이 표준적인 밸런스이고 데이터가 자주 바뀌지 않고 한 번 구축할 때 최상의 검색 품질을 뽑아내야하는 도메인에서는 256~512까지도 튜닝합니다. 이 값은 검색 속도에는 영향을 주지 않고 오직 인덱스 생성 속도와 최종 검색 정확도에만 관여합니다.
- efSearch : 검색시 후보군을 얼마나 샅샅이 뒤질지 결정합니다. 검색시에 limit보다 크게 설정해야하는데 예를 들어서 10개를 찾고싶다면 efSearch를 64이상으로 설정해야합니다. 평소에는 낮게 유지하다가 정확도가 필요한 특정 쿼리에만 높여서 동적으로 대응할 수도 있습니다.
- Product Quantization : 고차원 벡터를 몇 개의 묶음으로 나눌지 결정합니다. 이때 이 값은 차원수의 약수여야 에러가 발생하지 않습니다. 세그먼트 수가 많을수록 정확도는 원본에 가까워지지만 압축률은 떨어집니다. 보통 8차원당 1세그먼트가 적정값이고 PQ를 적용하면 메모리 사용량을 원본 대비 4배에서 64배까지 줄일 수 있고 CPU 캐시 효율이 좋아져 검색 속도가 오히려 빨라지기도 합니다. 다만 데이터가 5만건 이하라면 굳이 사용하지 않아도 되기 때문에 신중히 결정해야합니다.
마치며
쓰기 전에도 꽤 큰 볼륨이라고 생각했는데 쓰고 나니까 두개로 나눠서 쓸걸 그랬나 살짝 후회가됩니다. 이번 포스팅에서는 벡터 데이터베이스와 관련된 제반 지식들을 공부해보고 정리해봤습니다.
개인적으로 새로운 분야를 공부하는 것을 별로 좋아하진 않습니다만 이건 나름대로 흥미가 가는 부분들이 있어 공부하면서 꽤나 재밌게 공부했던 것 같습니다.
특히 Pre-filtering에서 생기는 단점들을 벡터 데이터베이스마다 다르게 해결하는 방식을 공부한 부분이 개인적으로 가장 재밌었습니다. 저는 문제 상황을 비슷한 기술들끼리 어떻게 해결했는지를 공부할 때 제일 흥미로워하는 것 같습니다.
일례로 CAP이론을 NoSQL이 임하는 방식을 보면 어떤 데이터베이스는 CP를 더 중요하게 생각하고 어떤 데이터베이스는 AP를 더 중요하게 생각하고 왜 그렇게 생각하고 어떤 결과로 이어지고 어떤 상황에서 어떤 데이터베이스를 선택해야하는지 등등 꼬리에 꼬리를 물고 사고가 뻗어나가게 되는 부분이 재밌는 것 같습니다.
이제 슬슬 포스팅을 줄여보도록 하겠습니다. 긴 글 읽어주셔서 감사합니다. 오늘도 즐거운 하루 되세요!
'CS 지식 > 데이터베이스' 카테고리의 다른 글
| 전통적인 ACID와 분산 시스템에서의 ACID (0) | 2025.07.12 |
|---|---|
| 깔끔쟁이 MySQL (InnoDB) vs 게으름뱅이 PostgreSQL (0) | 2025.05.04 |
| PostgreSQL의 1순위 장애 이유 Auto Vacuum Bloating (0) | 2025.04.29 |
| NoSQL의 두 얼굴 : CP 데이터베이스와 AP 데이터베이스 (0) | 2025.03.20 |
| 왜 MongoDB인가? (0) | 2025.03.20 |