본문 바로가기
프로젝트/개인 프로젝트(2023.11.13-2023.12.18)

[개인프로젝트] 검색기능에 대한 생각 및 회고 (1)

by dal_been 2024. 3. 5.
728x90

최근 검색기능에 대한 생각이 많아지면서 정리해보기로 한다.

 


개인프로젝트에서 검색기능을 구현했다.

간단하게 말하자면 위치기준, 이름 기준으로 검색하면

 

  private Long id;
  private String walkerName;
  private Double walkerLnt;
  private Double walkerLat;

 

이렇게 반환값이 나오게했다.

 

다만 나는 조금더 검색을 빠르게 하고 싶었다. 그래서 어떻게 할까 생각했을때 두가지 방법이 생각이 났다.

 

Mysql Index, ElasticSearch 두가지중 ElasticSearch를 이용했다.

Index의 경우 공부해 본적이 없어서 어떤 것을 인덱스를 걸어야할지... 인덱스를 어떻게 생성해야할지 에 대한 고민이 많았다.

근데 ElasticSearch의 경우 자바코드가 mysql에 데이터 반영하는 것과 매우 흡사했다. 기본적으로 ElasticSearch가 어떻게 빠르게 검색을 하는지 에 대해 이해하면 쉽게 적용할 수 있었다.

추가적으로  Mysql Index를 걸었을때와 ElasticSearch 검색속도를 비교해봤다. 확실히 ElasticSearch가 빨랐다.

그래서 ElasticSearch를 사용했다.

 

근데 시간이 지나다보니... ElasticSearch에 대한 생각이 많아졌다.

1. 관리해야할 부분이 증가했다.

-> 결국 어떻게 보면 ElasticSearch도 DB와 비슷했다. 값이 잘 들어갔는지,,, 의도한대로 쿼리가 나오는지,,, 변경 사항에 대해서는 어떻게 적용이 잘되는지 지속적인 확인이 필요했다.

2. 생각보다 서버에 부하가 많이 되었다.

-> 협업프로젝트에서도 팀원분이 ElasticSearch를 통해 검색을 구현하셨는데 ElasticSearch랑 Kibana를 Docker image로 실행시키자마자 바로 서버가 다운되버렸다.. 물론 현업에서는 docker가 아니라 따로 만들겠지만..

3. 이전 데이터 반영 시간

-> 만약 서비스를 운영중이다가 이전 데이터를 ElasticSearch에 반영한다고 했을때 시간이 오래걸릴것이다. 나의 경우 테스트를 위해 20만개 데이터를 넣었는데 약 30분 걸렸다...

 

그래서 고민을 하다가.,,캐시라는 방법이 생각났다.

이미 Redis가 구축되어있다면 Mysql Index와 캐시를 사용한다면 ElasticSearch만큼의 성능이 나오지 않을까? 생각했다.

 


 

 

캐시의 정의를 생각해보면

"한번 조회된 데이터를 어느 공간에 저장해놓고 똑같은 요청이 발생하면 DB에서 찾는게 아니라 미리 저장해놓은 데이터를 제공하는 것"

 

왜 캐시라는게 생겼을 까 조금더 고민해보자면

앞서 이야기한 검색속도 개선도 있지만 DB적인 이유도 존재한다.

DB는 데이터를 물리 디스크에 직접 쓰기때문에 서버에 문제가 발생해도 데이터가 손실되지는 않지만, 매 트랜젝션마다 디스크에 접근해야하므로 부하가 많아지면 성능이 떨어진다. 그래서 사용자 증가시 스케일인, 스케일 아웃하는 방식외 캐시가 고려되는 것이다.

 

어떻게 내 프로젝트에 캐시를 적용해볼 수 있을까..?

첫번째 고민 : Redis에 모든 서비스 수행자 저장??

 

검색시에 내 위치 반경 3Km+ 이름기준 수행자를 검색 하게 된다. 그렇게 했을때 반환값이 밑에 와 같아야한다.

  private Long id;
  private String walkerName;
  private Double walkerLnt;
  private Double walkerLat;

 

그럼 Redis에 여러 수행자를 저장하게된다.

다만 여기서 문제점이 수행자 이름 기준 검색은 항상 달라질 수 있다는 점이다. 그렇다면 결국 Redis에 찾고자하는 값이 없을 경우 가 많기에 cache miss가 되는 경우가 많다.

 

그래서 생각한게 그럼 서비스 수행자가 가입할때마다 Redis에 저장할까??

아 이건 아니다... 그럴꺼면 Database가 왜 있느냐.. 그냥 Redis에만 저장하지..

또한 Redis의 메모리가 거의 Database 만큼 좋아야한다고 생각한다. 모든 수행자를 저장해야하니까..

뿐만 아니라 캐시라는 개념에서 벗어난다고 생각한다. 자주 검색되는 부분을 DB를 거치지 않기 위해 사용되는 데 그냥 모든 검색을 빠르게 하겠다고 Redis를 약간 남용하는 느낌이다.

 

두번째 고민 : 위치기준만 저장..?

 

내 서비스를 봤을때 가장많이 검색되는 게 자신 위치기준이라고 생각한다.

사실 특정 이름으로 검색하는 거는 어느정도 서비스를 이용해봤고 단골 서비스 수행자가 생겨 해당 수행자만을 찾기 위하는 경우라고 생각한다.

즉 이름은 null값이고 자신 위치를 바탕으로 검색되는 경우가 많다는 것이다.

 

그럼 Redis에 이름 검색 제외 내 위치에서 3km이내 서비스 수행자들을 Redis에 캐시한다면 검색속도가 빨라지지 않을까??

또한 Redis 캐시시 만료기간을 줘서 어느 정도 시간이 지나면 삭제되는 것도 건다면..?(서비스를 24시간동안 검색하는게 아니기 때문에)

 

오.. 이방법이 옳은 것같다. 서비스의 검색 방향성?과 Redis의 캐시 방향성과 맞는 것같다.

 

세번째 고민 : Redis 캐시만이 방법일까?

 

알아본 바로는 캐시에는 Redis, Memcached, 스프링 로컬 캐시가 존재한다.

 

일단 Redis에 대해서 말해보자면

  • 문자열, 해시 , 목록, 세트 등 복잡한 데이터 유형을 저장가능
  • 메모리와 디스크에 저장되어 영속성 지원가능
  • key- value 구조의 인메모리 비관계형 데이터베이스
  • 만료일 지정시 데이터 삭제됨
  • 트랜젝션 지원

Memcached

  • 문자열만 저장가능
  • 데이터가 메모리에만 저장되어 프로세스 종료시 사라짐
  • key- value 구조의 인메모리 비관계형 데이터베이스
  • 만료일 지정시 데이터 삭제됨
  • 트랜젝션 지원하지 않음
  • Redis 보다 처리 속도가 빠르다

두 캐시 방식의 큰 차이점은 사실 얼마나 많은 자료구조를 지원하냐? 일 것같다.

 

스프링 로컬 캐시 

  • 일반적으로 데이터를 JVM Heap에 저장하는 메모리 내 캐시
  • 외부 종속성이 필요없음

스프링 로컬캐시는 내부 메모리를 이용하기 때문에 접근속도가 매우 빠른다. 다만 문제점이 Heap공간에 저장되어 메모리 부족상태를 만날 수 도 있고 인스턴스가 여러개인 경우 A인스턴스에는 캐시가 존재하지만 B인스턴스에 는 없을 수 있는 일관성 문제가 생긴다. 그래서 일단 스프링 로컬캐시는 제외하고..

 

Redis와 Memcached 중에 고민을 해봤다.

Redis가 맞다고 생각한다.

일단 다양한 자료구조를 지원한다는 점이다. 나의 검색기능의 경우 List로 산책 수행자들을 저장해야한다.

근데 Memcached는 String타입을 지원한다. Memcached가 여러 자료 구조를 지원했다면 아마 더 고민했을 것이다.

(왜냐하면 단순하게 위치기준으로만 검색하는 것이고 캐시기능이기에 영속성을 지원할 필요가 없다고 생각했다. )


 "Mysql Index와 캐시를 사용한다면 ElasticSearch만큼의 성능이 나오지 않을까? " 에 대한 결과는 

 Mysql Index를 이용하든 ElasticSearch를 이용하든 캐시를 이용하면 검색 속도를 둘다 향상시킬 수 있다.

 

그래서 다음 내용은 Redis 캐시를 사용하여 간단하게 코드를 짜볼 것이다.