Vector Search ๋ฒค์น๋งํฌ (pgvector, elasticsearch, qdrant)
Vector Search๋ฅผ ์ฝ๊ฐ ์คํ์ ์ผ๋ก ๋์ ํด๋ณผ ํ์๊ฐ ์์๋๋ฐ, ๋ฆฌ์์ค๋ฅผ ๋๋ต์ ์ผ๋ก ์ผ๋ง๋ ์๊ตฌํ๋์ง, ์ฑ๋ฅ์ ์ด๋ค์ง ๋ฑ์ ํ์ธํด๋ณด๋ ค๊ณ ๊ฐ๋จํ๊ฒ ๋ฒค์น๋งํฌ๋ฅผ ๊ตฌ์ฑํด์ ๋๋ ค๋ดค๋ค.
์์๋๋ ์ด๊ธฐ ๋ฐ์ดํฐ ํฌ๊ธฐ๋ 1000๋ง๊ฐ ์ ๋.
๋ฒกํฐ์ ํฌ๊ธฐ๋ 256, dot product๋ฅผ ์ฌ์ฉํ ์์ ์ด์๋ค.
์ฒ๋ฆฌ ์๊ฐ์ ๋น ๋ฅด๋ฉด ๋น ๋ฅผ์๋ก ์ข์ง๋ง, ๋ฐ๋ฆฌ์ด ๋จ์๋ง ๋ณด์ฅ๋๋๋ผ๋ ์ถฉ๋ถํ๋ค.
์์คํ ๋ฆฌ์์ค๋ ์ ์ผ๋ฉด ์ ์์๋ก ์ข์๋ค. ํ์คํ ์ ์ฉ์ฑ์ด ์์ง ๊ฒ์ฆ๋์ง ์์์ผ๋ฏ๋ก, ์ผ๋จ ๊ฐ๋ณ๊ฒ ์์ํด์ ๋ฐ์ดํฐ์ ๊ณผ ์ฌ์ฉ๋์ด ์ฆ๊ฐํ ๋ค์์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์ฎ๊ธฐ๋ ๊ฒ๋ ๊ฒํ ํด๋ณผ๋งํ๋ค.
๊ทธ๋์ ๋จ์ผ ๋ ธ๋์, ๋ฉ๋ชจ๋ฆฌ๋ 8GB, CPU๋ ๋จ์ผ์ฝ์ด๋ก ๊ฐ์ ํ๊ณ ์ผ๋ง๋ ์ ๋ฒํฐ๋์ง๋ฅผ ๊ฒ์ฆํ๋ค.
ํ๋ณด๋ PostgreSQL(pgvector), Elasticsearch ์ ๋๋ค.
milvus ๊ฐ์ ์ ๋ฌธ vectordb๋ ์๊ฐ์ ํด๋ดค์ง๋ง, ์ถฉ๋ถํ ์ฌ์ฉ๋์ด ์๊ธด ๋ค์ ์ฎ๊ฒจ๋ ๋ฌด๋ฐฉํ๋ค๊ณ ํ๋จํ๋ค.
ํ
์คํธ์ ์ฌ์ฉํ ์ ์ฒด ์์ค์ฝ๋๋ ๊นํ์ ์๋ค.
https://github.com/myyrakle/benchmarks/tree/master/vector_search
๋ฒกํฐ ์์ฑ
๋จผ์ ํ ์คํธ์ ์ฌ์ฉํ ๋ฒกํฐ์ด 1000๋ง๊ฐ๋ฅผ ์ฌ์ ์ ์์ฑํ๋ค.
use rand::Rng;
use std::error::Error;
use std::fs::OpenOptions;
use std::io::{BufWriter, Write};
use std::time::Instant;
const NUM_VECTORS: usize = 10_000_000;
const VECTOR_DIM: usize = 256;
const OUTPUT_FILE: &str = "vectors.txt";
fn main() -> Result<(), Box<dyn Error>> {
println!(
"Generating {} vectors of dimension {}...",
NUM_VECTORS, VECTOR_DIM
);
let start_time = Instant::now();
let file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(OUTPUT_FILE)
.unwrap();
let mut writer = BufWriter::new(file);
let mut randomizer = rand::rng();
for i in 0..NUM_VECTORS {
let vector: Vec<f32> = (0..VECTOR_DIM)
.map(|_| randomizer.random_range(-1.0..1.0))
.collect();
let magnitude: f32 = vector.iter().map(|&val| val * val).sum::<f32>().sqrt();
// ๊ฐ ์์๋ฅผ ํฌ๊ธฐ๋ก ๋๋์ด ์ ๊ทํ
let normalized_vector: Vec<half::f16> = vector
.iter()
.map(|&val| half::f16::from_f32(val / magnitude))
.collect();
// ๋ฒกํฐ๋ฅผ ๋ฌธ์์ด ๋ ์ฝ๋๋ก ๋ณํ
let record: Vec<String> = normalized_vector
.iter()
.map(|&val| val.to_string())
.collect();
// ์งํ ์ํฉ ํ์ (์ ํ ์ฌํญ, ์ฑ๋ฅ์ ์ฝ๊ฐ ์ํฅ ์ค ์ ์์)
if (i + 1) % 1_000_000 == 0 {
println!("Generated {} vectors...", i + 1);
}
let row_string = record.join(",");
// ํ์ผ์ ๋ฒกํฐ ๋ฌธ์์ด์ append๋ก ์ ์ฅ
writer.write_all(row_string.as_bytes())?;
writer.write_all(b"\n")?;
}
let duration = start_time.elapsed();
println!(
"Successfully generated and saved {} vectors to {}",
NUM_VECTORS, OUTPUT_FILE
);
println!("Total time taken: {:?}", duration);
Ok(())
}
๋ฝ์ผ๋๊น 7๊ธฐ๊ฐ ์ ๋์ ํ
์คํธ๊ฐ ๋์๋ค.
DB ์ค์
๋ฐ์ดํฐ๋ฒ ์ด์ค๋ pgvector(hnsw์ฉ), pgvector(ivfflat์ฉ), elasticsearch ์ ๋๋ง ๊ณ ๋ คํ๋ค.
๋์ปค ์ปจํ ์ด๋ ๊ธฐ๋ฐ์ผ๋ก ํ ์คํธ ํ๊ฒฝ์ ๊ตฌ์ฑํ๋ค.
services:
pgvector-IVFFlat:
image: pgvector/pgvector:0.8.0-pg17
container_name: pgvector-ivfflat
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=q1w2e3r4
- POSTGRES_DB=postgres
ports:
- "15432:5432"
volumes:
- pg-IVFFlat:/var/lib/postgresql/data
deploy:
resources:
limits:
cpus: "1"
memory: 8096M
pgvector-HNSW:
image: pgvector/pgvector:0.8.0-pg17
container_name: pgvector-hnsw
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=q1w2e3r4
- POSTGRES_DB=postgres
ports:
- "15433:5432"
volumes:
- pg-HNSW:/var/lib/postgresql/data
deploy:
resources:
limits:
cpus: "1"
memory: 8096M
volumes:
esdata:
driver: local
pg-IVFFlat:
driver: local
pg-HNSW:
driver: local๋ฐ์ดํฐ ์ด๊ธฐํ
์ธ๋ฑ์ค๋ฅผ ์ ๋ถ ๊ตฌ์ฑํด๋์ ์ํ์์ ๋ง๋ค์ด๋๋ ๋ฒกํฐ 1000๋ง๊ฐ๋ฅผ ๋ฐ์ด๋ฃ์๋ค.
๋จ์์ฑ์ ์ํด ์์งํ๊ฒ ํ๋์ฉ ์์๋๋ก ๋ฃ๋ ๋ฐฉ์์ ์ทจํ๋ค.
๊ทธ๋ ๊ฒ ํ์๋
pgvector(IVFFLAT)๋ 9์๊ฐ ์ ๋ ๊ฑธ๋ ธ๊ณ
pgvector(hnsw)๋ ํนํ๋ ์ค๋ ๊ฑธ๋ ธ๋ค. ๋ฉ๋ชจ๋ฆฌ ๋ณ๋ชฉ์ ๊ฑธ๋ฆฐ๊ฑด์ง ๋ญ์ง๋ ์ ๋ชจ๋ฅด๊ฒ ๋ค.

ํ์คํ๊ฑด ์ ๋ฐ์ ์ธ ๋ฆฌ์์ค ์๋ชจ๋์ด ์๋์ ์ผ๋ก ๋ง์๋ค๋ ๊ฒ์ด๋ค. ๋์คํฌ I/O๋ ๋ฌด์จ ํ
๋ผ๊ธ์ผ๋ก ์๋ชจํ๋ค.

pgvector(hnsw)๋ 3์ผ ์ ๋ ๊ฑธ๋ ธ๋ค.
elasticsearch๋ 25์๊ฐ์ด ๊ฑธ๋ ธ๋ค.

ํ์์ ๋ฆฌ์์ค ์ฌ์ฉ๋์ ๋๋ต ์ด๋ ๋ค.
ivfflat์ด ์ ๋
๋ฆฌ์์ค๋ฅผ ์ ๊ฒ ๋จน๋ ํธ์ด๊ธด ํ๋๋ผ.

qdrant๋ ์ฝ์
์๋๋ ์ค์ํ๊ณ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋๋ ๋ฎ์๋๋ฐ, ๊ฐ๋ CPU๊ฐ ํ
์น๊ณ yellow๊ฐ ๋จ๋ ๊ตฌ๊ฐ์ด ์์๋ค. ๋ญ๊ฐ ๋ชจ์์ ํ๋ฒ์ ๋์คํฌ์ ์์ถํ๊ฑฐ๋ ํ๋ ํจํด์ด ์๋ ๊ฒ ๊ฐ๋ค.
๋ค๋ง
์ค์ ์์ฉ ํ๊ฒฝ์์๋ ์ด๊ธฐํ์ ๊ฑธ๋ฆฌ๋ ์๊ฐ์ด ์ด์๋ ํฌ๊ฒ ๋ค๋ฅผ ๊ฒ์ด๋ค.
์ผ๋จ ๋ฒกํฐ ์๋ฒ ๋ฉ์ ๋ชจ๋ธ๋ก๋ถํฐ ๋ฐ์์ค๋ ๋๋ ์ด๊ฐ ์กด์ฌํ ๊ฒ์ด๋ค. ์ด๊ฑด ๋๋ ค์ง ์ ์๋ ์์ธ์ด๋ค.
๋ณดํต์ DB ํ๊ฒฝ์์๋ ์ด๋ณด๋ค ๋ฆฌ์์ค๊ฐ ๋๋ํ ๊ฒ์ด๋ค. ๋๋ 1cpu๋ก ๋นก์ธ๊ฒ ๊ฑธ์ด๋์ ์ค๋กํ๋ง์ด ๊ณผํ๊ฒ ๊ฑธ๋ ธ์ ๊ฒ์ด๋ค. ๋ณดํต ๋ฉ๋ชจ๋ฆฌ๋ฅผ 8 ์ก์ผ๋ฉด ์ฝ์ด๋ 2~4๊ฐ์ฏค ๋ฃ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ฐ์ดํฐ ์ถ๊ฐ์์๋ batch ๋จ์๋ก ๋ชจ์์ insert๋ฅผ ํ๋ฉด ์ฒ๋ฆฌ์๊ฐ์ ์ข ๋์ผ ์ ์๋ค.
(pgvector์ ๊ฒฝ์ฐ) ์ธ๋ฑ์ค๋ฅผ ๋ง๋ค๊ธฐ ์ ์ ๋ฒกํฐ๋ฅผ ๋ฐ์ด๋ฃ๊ณ , ๋ฐ์ดํฐ๊ฐ ์๋ ์ํ์์ ์ธ๋ฑ์ค๋ฅผ ํ๋ฒ์ ์์ฑํ ์๋ ์๋ค. ์์ปค ๋ฉ๋ชจ๋ฆฌ ์ถฉ๋ถํ ์ฃผ๊ณ ๋๋ฆฌ๋ฉด ์ด๋ณด๋ค๋ ๋นจ๋ฆฌ ๋ ๊ฒ์ด๋ค.
์, ๊ทธ๋ผ ์ค๋น๊ฐ ๋์ผ๋ ๋ฒค์น๋ฅผ ์ ๋นํ ์ฐ๋ฌ๋ณด๊ฒ ๋ค.
๋์คํฌ ์ฌ์ฉ๋
๋์คํฌ ์ฌ์ฉ๋์ DB๋ณ๋ก ์ฐจ์ด๊ฐ ์ข ๋๊ธด ํ๋ค.


elasticsearch๊ฐ ๊ฐ์ฅ ๋ง์ ๊ณต๊ฐ์ ์ฐจ์งํ๊ณ
qdrant๊ฐ ๊ทธ๋๋ ๋ฒกํฐ ์ ๋ฌธ์ด๋ผ๊ณ ๊ฐ์ฅ ์ปดํฉํธํ๊ฒ ์ผ๋ค.
pgvector(IVFFLAT) ๋ฒค์น๋งํฌ
๋ก์ง์ ๋ณ๋ก ํน๋ณํ ๋ถ๋ถ์ ์๋ค.
์ฟผ๋ฆฌ๋ 100๋ฒ ์ ๋๋ง ์ฐ๋ฌ๋ณด๊ฒ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋์์ฒ๋ฆฌ ํ์คํฌ๋ 16๊ฐ ์ ๋๋ง ์ก์๋ค.
๊ฐ์ ธ์ฌ ๋ฐ์ดํฐ๋ 10๊ฐ๋ก ์ก์๋ค.

์ต๋ ๋ ์ดํด์๊ฐ 300ms ์ ๋๊น์ง ๋์์์์ฒญ ๋น ๋ฅด๋ค๊ณ ํ ์ ๋๋ ์๋์์ง๋ง, ํ๊ท ์ฒ๋ฆฌ์๋๋ ํฌ๊ฒ ๋์์ง ์์๋ค.
DB์ธก์ CPU ๋ฆฌ์์ค ์๋ฐ์ ์ฝ๊ฐ ์์๋ค.
pgvector(HNSW) ๋ฒค์น๋งํฌ
hnsw๋ ๊ฝค๋ ๋น ๋ฅด๊ณ ์์ ์ ์ด์๋ค.

ํ์คํ ๋ ์ดํด์๊ฐ lvfflat์ ๋นํด์ ์งง๊ณ ์์ ์ ์ด์๋ค.
elasticsearch ๋ฒค์น๋งํฌ
elasticsearch๋ ๋น์ทํ๊ฒ ๊ตฌ์ฑํด์ ๋ฒค์น๋ฅผ ๋๋ ธ๋ค.

์๊ฐํ๋ ๊ฒ ๋ณด๋ค๋ ๋ ์ดํด์๊ฐ ์ ์กฐํ๋ค.
์ค๋๋ฅผ ๋๋๊ณ , ์ ํ๋๋ฅผ ๋ฎ์ถ๊ณ , ์ฑ๋ฅ์ ๋์ด๊ฒ๋ ์ต์ ์ ์กฐ์ ํ๋ฉด ์ฑ๋ฅ์ด ๋ ๋์ค๊ฒ ์ง๋ง, ์ผ๋จ์ ์ฌ๊ธฐ์ ๊ธฐ๋ณธ์ ์ธ ๋จ์ผ๋ ธ๋ ํ ์คํธ๋ก ๋ง๋ฌด๋ฆฌํด๋ณด๋ ค๊ณ ํ๋ค.
qdrant ๋ฒค์น๋งํฌ
qdrant๋ ์ ์ฉ VectorDB์ธ ๊ฒ์ ๋นํด ํ๊ท ์ฒ๋ฆฌ๋์ ์๊ฐ๋ณด๋ค ๊ทธ๋ฆฌ ๋น ๋ฅด์ง๋ ์์๋ค.
3์๋ฆฌ ๋ฐ๋ฆฌ์ด ์ ๋๋ก ์ฒ๋ฆฌ๊ฐ ๋์๋ค.
๊ทธ๋ฆฌ๊ณ ๋๋ CPU ๋ฆฌ์์ค๋ฅผ ์กฐ๊ธ ๋จน๊ธด ํ๋ค.
์ด๊ฒ๋ ํจ์จ์ ์ผ๋ก ์ฐ๋ ค๋ฉด ์ต์ ์กฐ์ ์ด ํ์ํ ๊ฒ ๊ฐ๋ค.
์ ๋ฆฌ
ํน์ดํ๊ฒ๋ ๋ ์ดํด์๋ง ๋ณด์๋ฉด pgvector-hnsw๊ฐ ๊ฝค๋ ๋นจ๋๋ค. elasticsearch์ qdrant ๊ฐ์ ๊ฒฝ์ฐ์๋ ๋ญ๊ฐ ๋ฆฌ์์ค ์๊ตฌ๋์ด ๋ ๋ง์์์ธ์ง ๋ชจ๋ฅด๊ฒ ๋๋ฐ, ์ด ์ ๋ ๊ฐ๋ฐํ ์ฌ์์์๋ ์์ฃผ ๊ทธ๋ ๊ฒ ๋น ๋ฅด์ง๋ ๋ชปํ๋ค.
๋ค๋ง qdrant๊ฐ ๋ ์ดํด์๋ณด๋ค๋ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์์ด์ ์ฅ์ ์ด ๋๋๋ฌ์ง ๊ฒ ๊ฐ๋ค. 1000๋ง๊ฐ ๋ฃ์๋๋ฐ๋ ํ์์ ๋ฉ๋ชจ๋ฆฌ ์ ์ ์จ์ด 1๊ธฐ๊ฐ ์กฐ๊ธ ๋๋ ์์ค์ด๋๋ผ. ๋ค๋ฅธ DB๋ค์ ํ์์์๋ ๋ฉ๋ชจ๋ฆฌ์ ๋๋ถ๋ถ์ ์ผ๋๋ฐ, ๋ฆฌ์์ค ํจ์จ์ฑ ์์ฒด๋ ์๋์ ์ด์๋ค.
์ถ๊ฐ DB ๊ตฌ์ฑ์ ๋ฐ๊ธฐ์ง ์๋๋ค๋ฉด pgvector-ivfflat๋ ๊ฝค๋ ์ธ๋งํ๊ฑฐ๊ฐ๋ค. ๋ฐ์ดํฐ ๊ท๋ชจ๊ฐ ์ปค์ง๋ ์ต์ ์ ์กฐ์ ํ๋๊ฒ ๊น๋ค๋กญ๊ณ , ๋ ์ดํด์๊ฐ ์กฐ๊ธ ํ๋ค๋ ๋จ์ ์ ์๋ค.
์ด๊ธฐ๋ฒ์ ์์ "์ฌ์ ํํฐ๋ง" ๊ฐ์ ๊ณ ๊ธ๊ธฐ๋ฅ์ด ํ์ํ์ง ์๋ค๋ฉด pgvector๋ ๊ณ ๋ คํด๋ณผ๋งํ ๊ฒ ๊ฐ๋ค.
๊ธฐ์กด์ ๊ฒ์์์ง์ผ๋ก elasticsearch๋ฅผ ์ฐ๋ ์ํฉ์ด๋ฉด elasticsearch ๋ํ ๊ณ ๋ คํด๋ด์งํ๋ค.
ํ์ง๋ง ๋๋ค ์ฐ์ง ์์๊ฑฐ๋, ์ ๋ง ์์ฒ๋ง์ ๋์ด๊ฐ๋ ๋ฐ์ดํฐ์ ์ ์ ์ ๋ฆฌ์์ค๋ก ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ์ถ๋ค๋ฉด qdrant๊ฐ ์ข์ ์ ํ์ผ ๊ฒ ๊ฐ๊ธด ํ๋ค.
์ฐธ์กฐ
https://blog.naver.com/sssang97/223839344102
https://blog.naver.com/sssang97/223802994977