20180423

Redis 4 모듈 — RediSearch 시험

redis 4.0에서 모듈 API가 정식으로 들어오면서 RediSearch, ReJSON, ReBloom 같은 모듈이 쓸 만해졌다. RediSearch 1.0이 작년에 GA, 지금 최신이 1.0.9. 사내 관리자 페이지 제품 검색을 Elasticsearch에서 걷어낼 수 있을지 궁금해서 실험.

띄우기. 모듈 직접 빌드해서 --loadmodule로 붙이거나 redislabs 배포 이미지.

# redis.conf
loadmodule /usr/lib/redis/modules/redisearch.so

# or docker
docker run -p 6379:6379 redislabs/redisearch:1.0.9

인덱스 정의. 스키마 기반이라는 게 Elasticsearch의 mapping과 비슷. FT.CREATE로 필드별 타입(TEXT/TAG/NUMERIC/GEO)과 옵션(WEIGHT, SORTABLE, NOINDEX)을 지정.

FT.CREATE products
  SCHEMA
    name   TEXT WEIGHT 5.0 PHONETIC dm:en
    desc   TEXT WEIGHT 1.0
    brand  TAG  SEPARATOR ","
    cat    TAG  SEPARATOR "|"
    price  NUMERIC SORTABLE
    stock  NUMERIC
    loc    GEO

FT.ADD products p:1 1.0 FIELDS
  name  "acme drill 18v"
  desc  "cordless drill set with battery"
  brand "acme"
  cat   "tools|power"
  price 129.0
  stock 42
  loc   "-73.98,40.76"

FT.SEARCH products "drill @price:[50 150] @brand:{acme}" LIMIT 0 10

동작 원리 메모. 내부적으로 전형적인 역색인을 레디스 해시 자료구조로 구현. 각 토큰별로 posting list(문서 ID + 위치)를 압축해 저장하고, 쿼리 시 intersection을 byte-level로 뛰면서 스코어 계산. scoring은 기본 TF-IDF이고 SCORER로 BM25 전환 가능. 1.0.x 기준 BM25는 preview 수준이라 대체로 기본값 사용.

Aggregation 지원(FT.AGGREGATE)이 의외로 쓸 만함. GROUPBY/REDUCE로 패싯 카운트 뽑기 가능.

FT.AGGREGATE products "@brand:{acme}"
  GROUPBY 1 @cat
    REDUCE COUNT 0 AS cnt
  SORTBY 2 @cnt DESC
  LIMIT 0 20

벤치. 우리 레포 제품 110만 건.

  • 인덱싱 처리량: 약 9,500 docs/sec(1노드, 코어 4). bulk는 pipeline으로 1000건 단위. ES(5.6) 같은 머신 싱글 샤드 기준 약 14,000 docs/sec. 인덱싱은 ES가 우위
  • 검색 latency: 단일 키워드 쿼리 p50 1.3ms, p99 4.2ms. ES는 p50 3.1ms, p99 12ms. 복합 필터 있는 쿼리에서도 RediSearch가 대략 2배 빠름
  • 메모리: RediSearch가 전부 메모리. 110만 건 색인이 대략 1.9GB. ES는 JVM + OS page cache 합쳐 3GB 수준인데 디스크 기반이라 훨씬 큰 데이터를 다룰 수 있다는 장점
  • 디스크: RediSearch RDB 저장 시 850MB. ES는 세그먼트 포함 4.3GB

한계 — 우리 케이스 기준.

  • 한글 형태소 분석 없음. 1.0은 영어 + 중국어(실험적)만. 공백 기준 tokenize라 "무선 드릴"을 검색해도 "무선 드릴셋트" 같은 복합어가 매칭 안 됨. ES + nori(지금 개발 중) 혹은 은전한닢 같은 대안이 필요
  • Phonetic matching(PHONETIC dm:en)은 영어 대상. 영문 제품명 오타 내성에는 도움
  • Suggestion API(FT.SUGADD/SUGGET)는 별도 자료구조(Trie). 자동완성은 깔끔
  • 분산 검색 아직 없음. 1.0은 단일 노드. 샤드 분산은 Enterprise 전용 기능. 오픈소스 1.x에서는 앱 레벨에서 샤딩하거나 데이터 크기가 단일 노드에 맞을 때만

운영 주의. 모듈이 포함된 RDB는 동일 모듈이 로드된 노드에서만 로드 가능. master/replica가 버전 미스매치면 replication 깨짐. 배포 시 모든 노드 동일 .so 버전 고정 필수. 또 DEBUG RELOAD로 로컬 검증해도 모듈 상태까지 돌아오는지 매번 확인.

인덱스 파괴(FT.DROP)는 기본 옵션이면 색인만 제거하고 원본 해시(FT.ADD로 넣은 데이터)는 보존. 오히려 DD 플래그로 문서까지 지워야 완전 삭제. 실수 방지용으로 좋기도 하고, 인덱스만 재구성할 땐 편함.

결론: 영문 + 소규모(단일 노드에 들어가는) 검색이면 RediSearch가 latency/간결함에서 이긴다. 다국어 + 수천만 건 규모면 ES가 여전히 맞는 선택. 우리는 B2B 관리자 내부 도구 제품 검색(영문 SKU 위주, 12만 건) 한 군데 먼저 빼서 올려 보고 판단 예정.

댓글 없음: