Redis Cache 실습(Aka. Jedis, Cache Aside)

2024. 9. 14. 16:42·DB/Redis

이번 블로그 글에서는 Docker를 활용하여 MySQL 및 Redis 서버를 구축하고, 이를 Spring Boot, JPA, Redis (Jedis)와 함께 사용하여 간단한 캐싱 시스템을 구현하는 방법을 소개합니다. Cache Aside 패턴을 적용하여 데이터베이스에 저장된 사용자 데이터를 Redis에 캐싱하고, 성능을 향상시키는 방법을 실습합니다.


1. 개요**

  • 목표: MySQL에 저장된 사용자 데이터를 조회할 때, 이를 Redis에 캐싱하여 성능을 향상시키는 방법을 실습합니다.
  • 기술 스택:
    • Spring Boot: 애플리케이션 프레임워크
    • MySQL: 데이터베이스
    • Redis: 캐싱 솔루션
    • Jedis: Redis와의 통신을 위한 클라이언트
    • JPA: 데이터베이스 접근을 위한 ORM

2. 환경 설정

2.1. Docker를 이용한 MySQL 서버 구축

MySQL 서버를 Docker로 실행합니다.

docker run -d --name mysql-server -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=fastsns mysql:8.0
  • -p 3306:3306: MySQL의 기본 포트 3306을 호스트에 연결합니다.
  • MYSQL_ROOT_PASSWORD=root: MySQL의 root 계정 비밀번호를 root로 설정합니다.
  • MYSQL_DATABASE=fastsns: fastsns라는 데이터베이스를 생성합니다.

2.2. Docker를 이용한 Redis 서버 구축

Redis 서버를 Docker로 실행합니다.

docker run -d --name redis-server -p 6379:6379 redis
  • -p 6379:6379: Redis의 기본 포트 6379를 호스트에 연결합니다.

2.3. application.yaml

Spring Boot 애플리케이션에서 MySQL과 Redis를 연결하는 설정 파일입니다.

spring:
  application:
    name: jediscache

  datasource:
    url: "jdbc:mysql://localhost:3306/fastsns"
    username: root
    password: root

  jpa:
    hibernate.ddl-auto: create
    show-sql: true

  redis:
    host: localhost
    port: 6379

주요 설정:

  • datasource: MySQL 데이터베이스 연결 정보.
  • redis: Redis 서버 연결 정보.

2.4. build.gradle

Spring Boot 프로젝트에서 필요한 의존성을 설정합니다.

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.3.3'
    id 'io.spring.dependency-management' version '1.1.6'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'redis.clients:jedis:5.1.3'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    runtimeOnly 'com.mysql:mysql-connector-j'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

주요 의존성:

  • Jedis: Redis와의 통신을 위한 클라이언트.
  • Spring Data JPA: MySQL과의 데이터 통신을 담당.
  • MySQL Connector: MySQL과의 연결을 담당.

3. Redis 설정

RedisConfig 클래스

Redis와 Spring Boot를 연결하기 위한 JedisPool 설정을 정의합니다.

package com.example.jediscache;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

@Component
public class RedisConfig {

    @Bean
    public JedisPool createJedisPool() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setJmxEnabled(false);  // JMX 등록 비활성화
        return new JedisPool(poolConfig, "127.0.0.1", 6379);
    }
}

4. User 엔티티

User 엔티티는 MySQL에 저장될 사용자 정보를 나타내며, @CreatedDate 및 @LastModifiedDate를 사용해 생성일과 수정일을 자동으로 관리합니다.

package com.example.jediscache;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Getter
@EntityListeners(AuditingEntityListener.class)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(length = 50)
    private String name;

    @Column(length = 100)
    private String email;

    @CreatedDate
    @Column(name = "created_at")
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
}

5. UserController: Redis 캐싱 적용

UserController는 사용자 이메일을 조회하는 API를 제공합니다. Redis를 활용하여 캐시된 이메일 데이터를 먼저 조회하고, 캐시 미스 시 데이터베이스에서 조회한 후 캐시에 저장합니다.

package com.example.jediscache;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

@RestController
@RequiredArgsConstructor
public class UserController {
    private final JedisPool jedisPool;
    private final UserRepository userRepository;

    @GetMapping("/users/{id}/email")
    public String getUserEmail(@PathVariable Long id) {
        try (Jedis jedis = jedisPool.getResource()) {
            String userEmailRedisKey = String.format("users:%d:email", id);
            String userEmail = jedis.get(userEmailRedisKey);
            if (userEmail != null) return userEmail;

            userEmail = userRepository.findById(id).orElse(User.builder().build()).getEmail();
            jedis.setex(userEmailRedisKey, 30, userEmail);  // 캐시 TTL 30초
            return userEmail;
        }
    }
}

주요 기능:

  1. 캐시 조회: Redis에서 이메일 데이터를 먼저 조회.
  2. 캐시 미스 처리: 캐시에 데이터가 없을 경우, 데이터베이스에서 사용자 정보를 조회.
  3. 캐시에 저장: 데이터베이스에서 조회한 이메일을 Redis에 캐시하며, 캐시 TTL은 30초로 설정.

6. UserRepository

UserRepository는 JPA를 활용해 사용자 데이터를 관리하는 리포지토리 인터페이스입니다.

package com.example.jediscache;

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}

7. 애플리케이션 실행

Spring Boot 애플리케이션을 실행하면, MySQL 데이터베이스와 Redis 캐시를 사용해 이메일 데이터를 관리하는 시스템이 동작합니다. 캐시 적중(Cache Hit) 시 성능이 크게 향상되며, 캐시 미스(Cache Miss)일 경우 MySQL에서 데이터를 가져와 Redis에 캐시합니다.


8. API 테스트와 DB, Redis 간의 요청 및 응답 분석

이번 테스트는 Spring Boot, MySQL, Redis (Jedis)를 사용하여 간단한 이메일 조회 API를 구현하고, Redis를 활용한 캐싱 기능을 테스트하는 과정입니다. curl을 사용하여 여러 번 API를 호출하며 데이터베이스와 Redis 간의 상호작용을 확인했습니다.


8.1. 프로세스 흐름 요약

  1. 사용자는 /users/{id}/email API를 호출하여 특정 사용자의 이메일을 조회합니다.
  2. 서버는 우선 Redis에서 이메일을 조회하고, 캐시 미스가 발생하면 MySQL에서 이메일을 가져옵니다.
  3. MySQL에서 가져온 이메일을 Redis에 캐시하여, 이후 동일한 요청에 대해 Redis 캐시에서 빠르게 응답합니다.
  4. Redis는 TTL(Time To Live)을 30초로 설정하여, 캐시 데이터가 30초 동안 유지된 후 자동으로 삭제됩니다.

8.2. API 호출 및 로그 기록 분석

8.2.1. MySQL 데이터 확인

다음은 MySQL에 저장된 user 테이블의 데이터입니다. 이 데이터는 Redis 캐시에서 캐시 미스가 발생할 때 사용됩니다.

mysql> select * from user;
+----------------------------+----+----------------------------+----------+--------------------+
| created_at                 | id | updated_at                 | name     | email              |
+----------------------------+----+----------------------------+----------+--------------------+
| 2024-09-12 16:19:43.066219 |  1 | 2024-09-12 16:19:43.066219 | minsu    | minsu@gmail.com    |
| 2024-09-12 16:19:43.100124 |  2 | 2024-09-12 16:19:43.100124 | minyoung | minyoung@gmail.com |
| 2024-09-12 16:19:43.106684 |  3 | 2024-09-12 16:19:43.106684 | minil    | minone@gmail.com   |
| 2024-09-12 16:19:43.112250 |  4 | 2024-09-12 16:19:43.112250 | mine     | mine@gmail.com     |
+----------------------------+----+----------------------------+----------+--------------------+

8.2.2. Redis 명령 실행 및 캐시 키 확인

docker exec 명령어를 사용하여 Redis에 접속한 후, 캐시된 이메일 데이터를 확인하였습니다. 이 로그는 Redis가 정상적으로 이메일 데이터를 캐시하고 있음을 보여줍니다.

docker exec -it redis-server redis-cli
127.0.0.1:6379> keys *
1) "users:4:email"
2) "users:2:email"
3) "users:3:email"

8.2.3. Redis TTL(Time to Live) 확인

캐시된 이메일 데이터의 TTL을 확인한 결과, 캐시된 데이터의 TTL이 30초로 설정되었고 시간이 지남에 따라 TTL이 감소하는 것을 확인할 수 있었습니다.

127.0.0.1:6379> TTL users:4:email
(integer) 27
127.0.0.1:6379> TTL users:4:email
(integer) 22
127.0.0.1:6379> TTL users:4:email
(integer) 21

8.2.4. API 호출 로그 분석 (curl 요청)

다음은 curl 명령어로 /users/{id}/email API를 호출한 결과입니다. 캐시 미스와 적중 시의 동작을 분석합니다.

1. 캐시 미스 (MySQL에서 데이터 조회 및 Redis에 캐싱)
  • 처음 호출 시 Redis에 데이터가 없어 캐시 미스가 발생하고, MySQL에서 데이터를 조회하여 Redis에 저장합니다.
curl localhost:8080/users/4/email
mine@gmail.com%

docker exec -it redis-server redis-cli
127.0.0.1:6379> monitor
1726125638.554357 [0 172.17.0.1:64144] "GET" "users:4:email"      # 캐시 미스 발생 (데이터 없음)
1726125638.586310 [0 172.17.0.1:64144] "SETEX" "users:4:email" "30" "mine@gmail.com"  # MySQL에서 가져온 데이터 Redis에 캐싱
2. 캐시 적중 (Redis에서 데이터 바로 반환)
  • 두 번째 이후 동일한 API 요청 시, Redis에서 캐시된 데이터를 사용하여 빠르게 응답합니다.
curl localhost:8080/users/4/email
mine@gmail.com%
docker exec -it redis-server redis-cli monitor
1726125640.060030 [0 172.17.0.1:64144] "GET" "users:4:email"  # 캐시 적중
1726125642.632512 [0 172.17.0.1:64144] "GET" "users:4:email"  # 캐시 적중

8.3. 요청과 응답의 흐름

첫 번째 요청 (캐시 미스)

  1. API 요청: /users/4/email 호출.
  2. Redis 캐시 확인: GET users:4:email → 캐시 미스 발생.
  3. MySQL 조회: MySQL에서 사용자 id=4의 이메일 조회.
  4. Redis에 데이터 저장: 조회된 이메일 mine@gmail.com을 Redis에 30초 TTL로 저장 (SETEX).
  5. API 응답: mine@gmail.com.

두 번째 이후 요청 (캐시 적중)

  1. API 요청: 동일한 /users/4/email 호출.
  2. Redis 캐시 확인: GET users:4:email → 캐시 적중.
  3. API 응답: Redis에서 데이터를 반환하여 빠르게 응답 (mine@gmail.com).

8.4. 예상된 결과 vs 실제 결과

  • 예상된 결과:
    • 첫 번째 요청 시 캐시 미스가 발생하고, MySQL에서 데이터를 가져온 후 Redis에 저장.
    • 이후 30초 동안은 Redis에서 캐시된 데이터를 빠르게 반환.
  • 실제 결과:
    • 예상대로 첫 번째 요청에서 캐시 미스가 발생하고, 이후 API 요청에서는 Redis에서 캐시된 데이터가 적중하여 빠르게 응답되었습니다.
    • Redis TTL이 정확히 30초로 설정되어, TTL이 만료되기 전까지 데이터가 빠르게 캐시에서 제공되었습니다.

9. 결론

이번 테스트에서는 Cache Aside 패턴을 구현하여, Redis 캐시와 MySQL 데이터베이스 간의 효율적인 데이터 처리를 확인했습니다.

  1. 첫 번째 요청에서는 캐시 미스가 발생했으며, MySQL에서 데이터를 조회한 후 Redis에 저장되었습니다.
  2. 두 번째 이후 요청에서는 캐시 적중이 발생하여 Redis에서 데이터를 빠르게 제공했고, 데이터베이스의 부하를 줄였습니다.
  3. TTL 확인을 통해 캐시가 30초 동안 유지되며, 이후에는 데이터가 삭제되거나 새롭게 캐시된다는 점을 확인할 수 있었습니다.

Redis 캐시는 데이터 조회 성능을 크게 향상시키며, MySQL과 함께 사용될 때 효율적으로 작동함을 이번 실습에서 확인할 수 있었습니다.

저작자표시

'DB > Redis' 카테고리의 다른 글

Spring Boot Cache 실습  (1) 2024.09.14
Redis Cache 실습(Aka. Write Back)  (1) 2024.09.14
Redis Cache 활용법: 성능 최적화를 위한 캐시 전략  (0) 2024.09.12
Redis Key와 Scan 명령어: 대규모 데이터 검색  (0) 2024.09.11
Redis 트랜잭션: 안전한 데이터 변경 처리하기  (0) 2024.09.11
'DB/Redis' 카테고리의 다른 글
  • Spring Boot Cache 실습
  • Redis Cache 실습(Aka. Write Back)
  • Redis Cache 활용법: 성능 최적화를 위한 캐시 전략
  • Redis Key와 Scan 명령어: 대규모 데이터 검색
hyeseong-dev
hyeseong-dev
안녕하세요. 백엔드 개발자 이혜성입니다.
  • hyeseong-dev
    어제 오늘 그리고 내일
    hyeseong-dev
  • 전체
    오늘
    어제
    • 분류 전체보기 (282)
      • 여러가지 (107)
        • 알고리즘 & 자료구조 (72)
        • 오류 (4)
        • 이것저것 (29)
        • 일기 (1)
      • 프레임워크 (39)
        • 자바 스프링 (39)
        • React Native (0)
      • 프로그래밍 언어 (38)
        • 파이썬 (30)
        • 자바 (3)
        • 스프링부트 (5)
      • 운영체제 (0)
      • DB (17)
        • SQL (0)
        • Redis (17)
      • 클라우드 컴퓨팅 (2)
        • 도커 (2)
        • AWS (0)
      • 스케쥴 (65)
        • 세미나 (0)
        • 수료 (0)
        • 스터디 (24)
        • 시험 (41)
      • 트러블슈팅 (1)
      • 자격증 (0)
        • 정보처리기사 (0)
      • 재태크 (4)
        • 암호화폐 (4)
        • 기타 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    자바
    reactor
    취업리부트
    RDS
    프로그래머스
    Python
    AWS
    DP
    OOP
    #개발자포트폴리오 #개발자이력서 #개발자취업 #개발자취준 #코딩테스트 #항해99 #취리코 #취업리부트코스
    ecs
    WebFlux
    FastAPI
    mybatis
    celery
    spring
    SAA
    docker
    시험
    Spring WebFlux
    항해99
    백준
    그리디
    Docker-compose
    완전탐색
    java
    EC2
    파이썬
    Redis
    Spring Boot
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
hyeseong-dev
Redis Cache 실습(Aka. Jedis, Cache Aside)
상단으로

티스토리툴바