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

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

by dal_been 2024. 3. 5.
728x90

지난 블로그를 보면  검색에 대한 생각을 적어놨다.
ElasticSearch와 Mysql Index 에 대한 간단한 개인 의견을 적어놓았다.
추가적으로 캐시를 적용하면 어떻게 될까를 테스트 하기 위해 이번블로그를 적는다.


저번 블로그에서 "Mysql Index + 캐시를 사용한다면 ElasticSearch 만큼의 성능이 나오지 않을까" 에 대한 고민을 했었다.
고민의 결과는 Mysql, ElasticSearch 둘다 캐시를 사용하면 검색기능이 향상된다.
왜냐하면 캐시의 개념자체가 임시 메모리에 똑같은 요청에 대한 데이터를 저장하기에 캐시를 사용하면
Mysql를 사용하던 ElasticSearch를 사용하던 임시 메모리에서 캐시 데이터를 가져온다.
 
그래서 Redis를 이용한 캐시를 예제를 짜보았다.
 

Redis Config

 

@Configuration
@EnableCaching
public class RedisCacheConfig {


  @Value ("${spring.redis.host}")
  private String redisHost;

  @Value("${spring.redis.port}")
  private int redisPort;

  @Bean
  public RedisConnectionFactory redisConnectionFactory() {
    return new LettuceConnectionFactory(redisHost, redisPort);
  }
  @Bean
  public CacheManager testCacheManager(RedisConnectionFactory cf) {
    RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
        .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
        .entryTtl(Duration.ofMinutes(3L));

    return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(cf).cacheDefaults(redisCacheConfiguration).build();
  }
}

 
entryTtl를 통해 캐쉬를 3분동안 유지하도록 하였다.
 

Controller, Service, Repository

 

@RestController
@RequiredArgsConstructor
public class TestController {

  private final TestService testService;

  @GetMapping("/cache/{id}")
  public List <UserDto> getCache(@PathVariable Long id) {
    return testService.getUser(id);
  }

  @GetMapping("/noCache/{id}")
  public List <UserDto> getNoCache(@PathVariable Long id) {
    return testService.getUserNoCache(id);
  }
}

 
cache -> Mysql Index + Redis 캐쉬를 이용
noCache -> Mysql Index만 이용
 

@Service
@RequiredArgsConstructor
public class TestService {

  private final UserRepository userRepository;

  @Cacheable(cacheNames = "walkerList",key = "#id",unless ="#result ==null")
  public List <UserDto> getUser(Long id) {
    User user = userRepository.findById(id).get();
    double x = user.getLocation().getX();
    double y = user.getLocation().getY();
    String point = "POINT("+y+" "+x+")";
    List <User> usersWithinDistance = userRepository.findUsersWithinDistance(point, 500 , "test999%" ,
        PageRequest.of(0 , 10)).getContent();
  return usersWithinDistance.stream()
        .map(nowUser -> UserDto.from(nowUser))
        .collect(Collectors.toList());
  }

  public List <UserDto> getUserNoCache(Long id) {
    User user = userRepository.findById(id).get();
    double x = user.getLocation().getX();
    double y = user.getLocation().getY();
    String point = "POINT("+y+" "+x+")";
    List <User> usersWithinDistance = userRepository.findUsersWithinDistance(point, 500 , "test999%" ,
        PageRequest.of(0 , 10)).getContent();
    return usersWithinDistance.stream()
        .map(nowUser -> UserDto.from(nowUser))
        .collect(Collectors.toList());
  }

}

 
getUser -> Redis 캐싯 이용
getUserNoCache -> 캐시 이용 X
 

Repository

@Query(value = "SELECT * FROM users u WHERE " +
      "ST_CONTAINS(ST_Buffer(ST_PointFromText(:point, 4326), :buffer), u.location) " +
      "AND u.user_name LIKE :namePattern  ",
      nativeQuery = true)
  Page<User> findUsersWithinDistance(@Param("point") String point,
      @Param("buffer") int buffer,
      @Param("namePattern") String namePattern,
      Pageable pageable);

 


Redis Cache 이용하지 않고 MySQL 인덱스 이용할때 검색 속도

 

1회387
2회129
3회138
4회131
5회128
6회140
7회124
8회132
9회129
10회126

 
평균 156.4ms
 

Redis Cache 이용하고 Mysql 인덱스 이용할때 검색 속도

 

1회390
2회46
3회8
4회5
5회8
6회5
7회7
8회7
9회8
10회5

 
평균 : 48.9 ms
 
 
와... 그냥 캐시메모리에서 데이터 꺼내니까 확연히 단축되어버렸다..
 
백분율로 나타내면 (156.4-48.9)/156.4 * 100 = 68.75%
거의 68% 속도가 단축되었다..
 
물론 항상 cached hit한다는 보장은 없지만 최소한 반복되는 검색에 대해서는 확연히 단축됨을 볼 수 있다..
 (보장없다는 말은 모든 검색어에 대해서 캐시되는게 아니라서)


지금 캐시에 대해 정리해보니... 후회되는 점은 Mysql Index 와 Redis 캐시를 비교해볼껄..이다..
캐시를 생각치 못했긴 했지만  Mysql과 Redis를 이용하는 것이 기존 것을 활용한다는 점에서 비용과 시간 절약에 도움이 더 될것같다.
앞서 이야기 했듯이 ElasticSearch는 또하나의 DB라서 새로운 기술과 관리에 대한 비용이 발생한다.
음.. 일단 프로젝트 리팩토링 하고 캐시 적용은 조금만더 고민해보야할것같다..!