저장 전략: 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 - 벡터 검색 모듈

기술 개념

관련 글

요약

다양한 사용 사례에 최적화된 세 가지 저장 기술을 사용합니다:

NumPy (임베딩):

  • 빠른 벡터 연산(코사인 유사도)

  • 메모리 매핑

  • 배치 처리

  • 표준 ML 형식

JSON (구성):

  • 사람이 읽기 쉬움(쉬운 디버깅)

  • 버전 관리(Git diff)

  • 수동 편집(코드 필요 없음)

  • 범용 형식

Valkey (실시간 캐시):

  • 빠른 조회(마이크로초)

  • 벡터 검색(RediSearch)

  • TTL 만료(자동 무효화)

  • Pub/sub(실시간 업데이트)

하이브리드 접근법: 각 작업에 맞는 올바른 도구를 사용하고, 최적의 성능을 위해 결합합니다.


← 문서 인덱스로 돌아가기