DB/Redis

[Redis] MySQL 조회 속도 vs Redis 조회 속도

웅지니어링 2025. 3. 12. 16:31

* 개요

타 RDBMS보다 NoSQL인 redis의 속도가 훨씬 빠르다길래 궁금해서 한번 실습을 해보았다.

Framework는 Spring Boot를 사용, 더미 데이터는 100만 건을 넣었고, 간단한 게시글 조회로 실습을 진행하였다.

조회 속도 확인은 Postman을 통해 진행하였다.

 

* Repository 구현

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface BoardRepository extends JpaRepository<Board, Long> {
    Page<Board> findAllByOrderByCreatedAtDesc(Pageable pageable);
}

ORM은 JPA를 사용하였고, 최신 생성 시간 순으로 게시글을 조회할 수 있게 하였다.

 

* Service 구현 - MySQL 사용

import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class BoardService {
    private final BoardRepository boardRepository;
    
    public List<Board> getBoards(int page, int size) {
        Pageable pageable = PageRequest.of(page - 1, size);
        Page<Board> pageOfBoards = boardRepository.findAllByOrderByCreatedAtDesc(pageable);
        return pageOfBoards.getContent();
    }
}

 

* Controller 구현

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/boards")
@RequiredArgsConstructor
public class BoardController {
    private final BoardService boardService;

    @GetMapping()
    public List<Board> getBoards(
            @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size
    ) {
        return boardService.getBoards(page, size);
    }
}

1페이지의 10개의 게시글을 불러오도록 요청을 처리한다.

 

* 조회 속도 확인 - MySQL

평균적으로 700~800 ms가 소요되는 것을 확인하였다. 그렇다면 redis의 조회 속도는 어떨까?

 

* RedisConfig 구현

Redis를 사용하기 위해 config 패키지를 만든 후 RedisConfig를 만들었다.

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;

@Configuration
public class RedisConfig {
    @Value("${spring.data.redis.host}")
    private String host;

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

    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(new RedisStandaloneConfiguration(host, port));
    }
}

application.yml 파일에 설정해놨던 redis의 host와 port를 매핑해준다.

그 후 Lettuce라는 라이브러리를 활용해 Redis 연결을 관리하는 객체를 생성하고 Redis 서버에 대한 정보(host, port)를 설정한다.

 

* RedisCacheConfig 구현

Redis 캐싱을 위한 Config도 만들어준다.

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
@EnableCaching
public class RedisCacheConfig {
    @Bean
    public CacheManager boardCacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration
                .defaultCacheConfig()
                .serializeKeysWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(
                                new StringRedisSerializer()))
                .serializeValuesWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(
                                new Jackson2JsonRedisSerializer<Object>(Object.class)
                        )
                )
                .entryTtl(Duration.ofMinutes(1L));

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

RedisCacheConfiguration을 통해 구현을 하는데, redis에 key를 저장할 때 String 타입으로 직렬화해서 저장한다.

redis에 value를 저장할 때는 JSON으로 직렬화해서 저장한다.

마지막으로 데이터의 만료기간(TTL)을 설정해준다. 간단한 실습이기에 1분으로 설정하였다.

 

* Service 구현 - Redis 사용

Redis를 사용할 수 있게 service 로직을 수정한다.

import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class BoardService {
    private final BoardRepository boardRepository;

    @Cacheable(
            cacheNames = "getBoards",
            key = "'boards:page:' + #page + ':size:' + #size",
            cacheManager = "boardCacheManager"
    )
    public List<Board> getBoards(int page, int size) {
        Pageable pageable = PageRequest.of(page - 1, size);
        Page<Board> pageOfBoards = boardRepository.findAllByOrderByCreatedAtDesc(pageable);
        return pageOfBoards.getContent();
    }
}

@Cacheable 어노테이션을 붙이면 Cache Aside(Look Aside) 전략을 사용한다.

  1. 해당 메서드로 요청이 들어오면 redis에서 데이터가 존재하는지 확인한다.
  2. 데이터가 존재한다면 redis의 데이터를 조회해서 바로 응답을 할 것이고, 데이터가 없다면 메서드 내부의 로직을 실행시킨 뒤, return 값으로 응답한다.
  3. 그리고 return 값을 redis에 저장한다.
  • cacheNames : 캐시 이름 설정
  • key : redis에 저장할 key 이름 설정
  • cacheManager : cacheManager의 Bean 이름 지정

이제 Redis의 조회 속도를 확인해보자.

 

* 조회 속도 확인 - Redis

처음에 조회를 해보니 No cache entry라고 나온다.

redis에 데이터가 존재하지 않는다는 말이다.

그 후 board에 대한 응답을 처리한 뒤 redis에 데이터를 저장했을 것이다. 로그를 확인해보면,

redis에 데이터를 저장했음을 확인할 수 있다.

redis-cli를 실행시킨 뒤 redis에 데이터가 있는지 확인해보자.

keys * 명령어를 확인해보니 분명히 존재한다. 이제 TTL이 만료되기 전에 빨리 Postman에서 데이터를 조회해보자.

10 ms가 소요된다. MySQL과 Redis의 조회 속도를 비교해보면, 700 ms -> 10 ms 단축됐음을 알 수 있다.

그럼 항상 redis를 쓰는게 좋을까? 그건 아니다.

TTL을 통해 데이터가 주기적으로 삭제되는데, 자주 바뀌는 민감한 데이터에 대해서는 사용하기 어려울 것이다.

따라서 자주 조회되면서 잘 변하지 않는 데이터에 대해 redis를 쓰는게 효율적이며, 정책에 따라 TTL 설정을 잘 해야 한다!