저장 전략: Valkey vs JSON vs NumPy
이 글은 SEO 파이프라인과 검색 서비스에서 다양한 유형의 데이터에 대해 서로 다른 저장 기술을 사용하는 이유를 설명합니다.
문제: 하나의 솔루션이 모든 것을 해결하지 못한다
다른 데이터는 다른 접근 패턴을 가집니다:
-
임베딩 (5K x 384 float): 빠른 벡터 연산(코사인 유사도) 필요
-
구문 매핑 (2500개 이상의 구문): 사람이 편집 가능, 버전 관리 필요
-
쿼리 캐시 (실시간 쿼리): 빠른 키-값 조회, TTL 만료 필요
-
제품 데이터 (64K 제품): 구조화된 접근, 호환성 규칙 필요
모든 것에 동일한 저장소를 사용하는 것은 비효율적입니다.
세 가지 저장 기술
1. NumPy 배열: 벡터 연산
사용 사례: 임베딩 (제품, 쿼리, 구문)
NumPy를 선택한 이유:
-
빠른 벡터 연산: 행렬 연산을 위한 최적화된 C/Fortran 라이브러리
-
메모리 매핑 파일: RAM에 복사하지 않고 대용량 배열 로드 가능
-
배치 작업: 수천 개의 벡터를 밀리초 단위로 처리
-
표준 형식: ML 라이브러리(scikit-learn, TensorFlow)와 호환
파일 형식: 바이너리 .npy 파일
로딩:
import numpy as np
# 메모리 매핑 (전체 파일을 RAM에 로드하지 않음)
embeddings = np.load('embeddings.npy', mmap_mode='r')
# 코사인 유사도 계산
from sklearn.metrics.pairwise import cosine_similarity
similarities = cosine_similarity(query_embedding, embeddings)
성능: 실시간 추론에 적합한 속도로 대규모 유사성 검색 가능.
2. JSON 파일: 사람이 편집 가능한 데이터
사용 사례: 구성, 매핑, 메타데이터
JSON을 선택한 이유:
-
사람이 읽기 쉬움: 쉽게 검사하고 디버깅 가능
-
버전 관리: Git diff로 정확히 무엇이 변경되었는지 표시
-
수동 편집: 코드 없이 오류 수정 가능
-
범용 형식: 모든 언어가 JSON을 파싱할 수 있음
저장하는 내용:
-
구문-필터 매핑: 구문 → 필터 규칙
-
제품 특징: 제품 → 특징 딕셔너리
-
쿼리 메타데이터: 쿼리 → 클릭수, 노출수, 소스
-
파이프라인 구성: 단계 매개변수, 임계값
파일 형식: 텍스트 .json 파일
로딩:
import json
with open('phrase_mappings.json') as f:
mappings = json.load(f)
# 데이터 접근
filters = mappings['mini pc'] # ['form_factor:mini', 'category:pc']
3. Valkey (Redis): 빠른 키-값 캐시
사용 사례: 실시간 검색, 자동완성, 인기 쿼리
Valkey를 선택한 이유:
-
인메모리: 마이크로초 단위의 조회 지연 시간
-
RediSearch: 인덱스를 통한 벡터 유사성 검색
-
TTL 만료: 자동 캐시 무효화
-
Pub/sub: 서버 간 실시간 업데이트
-
지속성: 내구성을 위한 선택적 디스크 스냅샷
저장하는 내용:
-
쿼리 임베딩 캐시: 최근 쿼리 → 임베딩
-
인기 쿼리: 트래픽 기준 상위 1000개 쿼리
-
자동완성 인덱스: 접두사 → 쿼리 제안
-
필터 추출 캐시: 쿼리 → 추출된 필터
-
관련 검색어 캐시: 쿼리 → 관련 쿼리
데이터 구조:
-
문자열: 단순 키-값 (쿼리 → 임베딩)
-
정렬된 집합: 순위 데이터 (점수별 인기 쿼리)
-
RediSearch 인덱스: 벡터 유사성 검색
-
해시: 구조화된 데이터 (쿼리 메타데이터)
로딩:
from app.shared.valkey_cache import get_valkey
valkey = get_valkey()
# ... (구현 세부사항 생략)
결정 매트릭스
NumPy를 사용할 때
기준:
-
데이터가 숫자형(float, int)임
-
빠른 벡터 연산(내적, 코사인 유사도)이 필요함
-
데이터가 읽기 위주(거의 업데이트되지 않음)
-
데이터가 대용량(수백만 개의 숫자)
-
배치 처리(한 번에 많은 항목 처리)
예시:
-
임베딩 (제품, 쿼리, 구문)
-
특징 벡터
-
유사성 행렬
JSON을 사용할 때
기준:
-
데이터가 구조화됨(객체, 배열)
-
사람이 읽기 쉬워야 함
-
버전 관리(Git)가 필요함
-
데이터가 가끔 변경됨(수동 편집)
-
데이터가 소/중규모 (<100 MB)
예시:
-
구성 파일
-
구문 매핑
-
제품 메타데이터
-
파이프라인 매개변수
Valkey를 사용할 때
기준:
-
빠른 조회(마이크로초)가 필요함
-
데이터가 자주 변경됨(실시간 쿼리)
-
TTL 만료(캐시 무효화)가 필요함
-
Pub/sub(실시간 업데이트)이 필요함
-
벡터 검색(RediSearch)이 필요함
예시:
-
쿼리 캐시
-
자동완성
-
인기 쿼리
-
세션 데이터
-
속도 제한
하이브리드 접근법
최적의 성능을 위해 세 가지를 모두 결합합니다:
파이프라인 (오프라인 처리)
NumPy: 임베딩, 유사성 행렬 계산
JSON: 매핑, 메타데이터, 구성 저장
Valkey: 사용하지 않음(파이프라인은 오프라인 실행)
검색 서비스 (실시간 쿼리)
NumPy: 디스크에서 임베딩 로드(메모리 매핑)
JSON: 디스크에서 매핑 로드(메모리에 캐시)
Valkey: 실시간 쿼리, 자동완성, 인기 쿼리 캐시
데이터 흐름
graph LR
Pipeline[SEO 파이프라인
오프라인]
subgraph Storage
NP[NumPy 파일
embeddings.npy]
JS[JSON 파일
mappings.json]
end
Search[검색 서비스
실시간]
VK[Valkey
캐시]
Pipeline --> NP
Pipeline --> JS
NP --> Search
JS --> Search
Search --> VK
VK --> Search성능 비교
NumPy (메모리 매핑):
-
로드 시간: 즉시(가상 메모리에 제로-카피 매핑)
-
조회 시간: 거의 제로(배열 인덱스에 대한 직접 CPU 포인터)
-
메모리: 최소(OS 관리 페이지 캐시, 프로세스 RAM 상주 안 함)
JSON:
-
로드 시간: 높은 지연(순차적 파싱 및 객체 인스턴스화)
-
조회 시간: 효율적(표준 해시 맵 오버헤드)
-
메모리: 상당함(전체 직렬화 구조가 힙에 상주)
Valkey:
-
로드 시간: 영속적(백그라운드 서비스에 상주)
-
조회 시간: 중간(네트워크 왕복 및 프로토콜 직렬화 포함)
-
메모리: 외부화(데이터베이스 프로세스 내 격리)
승자: 고처리량 배치 작업에는 NumPy, 분산 단일 키 접근에는 Valkey
벡터 유사성 검색
NumPy (cosine_similarity):
-
배치 작업: 빠름(SIMD/벡터화 선형 대수 최적화)
-
병렬화 가능성: 높음(사용 가능한 모든 물리적 CPU 코어로 확장)
-
메모리: 선형(임베딩 차원에 직접 비례)
Valkey (RediSearch):
-
검색 속도: 우수함(전문화된 HNSW/벡터 인덱싱 활용)
-
병렬화 가능성: 제한적(엔진의 스레딩 모델에 제약)
-
메모리: 집중적(원시 임베딩 + 인덱싱 메타데이터 필요)
승자: 지각 임계값 미만의 실시간 검색에는 Valkey, 무거운 오프라인 분석 처리에는 NumPy
구성 조회
JSON:
-
로드 시간: 가변적(구성 복잡도에 비례)
-
조회 시간: 빠름(네이티브 딕셔너리 접근)
-
편집 시간: 마찰 없음(사람이 읽기 쉬운 텍스트 수정)
Valkey:
-
로드 시간: 즉시(연결 시 활성화)
-
조회 시간: 네트워크 제한(요청 오버헤드에 의존)
-
편집 시간: 프로그래밍 방식(CLI 또는 클라이언트 SET 명령 필요)
승자: 정적 구성에는 JSON, 동적 공유 상태에는 Valkey
참고 자료
기술
-
NumPy - 배열 컴퓨팅 라이브러리
-
JSON - 데이터 교환 형식
-
Valkey - 인메모리 데이터 저장소 (Redis 포크)
-
RediSearch - 벡터 검색 모듈
기술 개념
-
메모리 매핑 파일 - 위키백과
-
키-값 데이터베이스 - 위키백과
-
벡터 데이터베이스 - 위키백과
관련 글
-
SEO 파이프라인 개요 - 완전한 파이프라인 아키텍처
-
검색 서비스 아키텍처 - Valkey를 이용한 실시간 검색
-
다중 서버 아키텍처 - 서버별 저장소
-
증분 처리 - 캐싱 전략
요약
다양한 사용 사례에 최적화된 세 가지 저장 기술을 사용합니다:
NumPy (임베딩):
-
빠른 벡터 연산(코사인 유사도)
-
메모리 매핑
-
배치 처리
-
표준 ML 형식
JSON (구성):
-
사람이 읽기 쉬움(쉬운 디버깅)
-
버전 관리(Git diff)
-
수동 편집(코드 필요 없음)
-
범용 형식
Valkey (실시간 캐시):
-
빠른 조회(마이크로초)
-
벡터 검색(RediSearch)
-
TTL 만료(자동 무효화)
-
Pub/sub(실시간 업데이트)
하이브리드 접근법: 각 작업에 맞는 올바른 도구를 사용하고, 최적의 성능을 위해 결합합니다.