JAVA 17, Spring Boot 3.0.2, Gradle, MySQL, Redis로 만드는
'오늘의코딩' 메일링 서비스

 

 

데이터베이스 연결방식


이번 프로젝트의 큰 목표는 Redis와 MySQL을 둘 다 사용하는 부분이기 때문에 Redis Cache를 Write-Through 방식으로 사용하기로 했다. 프로젝트에서 두 데이터베이스에 업데이트를 한다고 해도 그 비중이 크지 않고, 예상되는 타겟층도 크지 않아서 성능상에 무리가 없을 것으로 예측하고 진행했다.

서비스 리스트를 한 번에 가져와서 Redis Cache를 이용하는게 RDB를 거치지 않아도 빠르게 접근가능한 부분을 많이 만들고 싶었고, 그 외에도 Cache에 두지 않고 RDB에 접근해야 하는 부분들도 있었기에 Write-Through 방식이 적합했다.

 

 

 

MySQL, Redis Connection Test


MySQL과 Redis 연결 test를 진행했다. 둘 다 잘 연결이 되어 있는지 확인하기 위해서 다음 코드들을 사용했고, 나와 같은 고민이 있던 사람들에게 도움이 되었으면 좋겠다.

 

 

build.gradle

implementation 'org.springframework.boot:spring-boot-starter-data-redis'
runtimeOnly 'com.mysql:mysql-connector-j'

testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1'

testImplementation 'org.springframework.boot:spring-boot-starter-test'

 

 

MySQL Connection Test

package com.toco.trialService.databaseTest;

import org.junit.jupiter.api.*;
import java.sql.Connection;
import java.sql.DriverManager;

public class DatabaseConnectionTest {
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/world";
    private static final String User = "root";
    private static final String Password = "0000";  // 임의의 패스워드

    @Test
    public void rdbConnectionTest() {
        try(Connection conn = DriverManager.getConnection(URL, User, Password)) {
            System.out.println(conn);
            // com.mysql.cj.jdbc.ConnectionImpl@3697186
        }catch(Exception e){
            System.out.println(e.getMessage());
        }
    }
}

 

 

 

Redis Connection Test

package com.toco.trialService.databaseTest;

import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import org.junit.jupiter.api.*;

import static org.springframework.test.util.AssertionErrors.assertTrue;

public class RedisConnectionTest {
    private RedisClient redisClient;
    private StatefulRedisConnection<String, String> connection;
    private RedisCommands<String, String> redisCommands;

    @BeforeEach
    public void beforeConnectDatabase(){
        redisClient = RedisClient.create("redis://localhost:6379");
        connection = redisClient.connect();
        redisCommands = connection.sync();
    }

    @AfterEach
    public void afterConnectDatabase(){
        connection.close();
        redisClient.shutdown();
    }

    @Test
    public void inMemoryConnectionTest() {
        assertTrue("Redis connection test failed", redisCommands.ping().equals("PONG"));
    }
}

 

 

Lettuce version 3.0.5에서는 더 이상 DefaultClientResources 클래스를 사용하지 않고, 새 Redis 클라이언트 객체 생성으로  RedisClient를 사용한다. 생성시에는 위 코드와 같이 create([url])을 사용하고, 사용이 끝난 시점에서는 shutdown(); 처리를 해주자.

 

 

Redis Caching 전략에 따른 고찰


Caching 전략으로 이걸 사용하는게 맞을까?

프로젝트를 만들면서 이런 고민을 제일 많이 했던 것 같다.

 

Redis Caching을 어떻게 활용할 것인가에 대한 고민이 많았다. 현업과 서비스에 따라 방식이 달라져야겠지만, 지금 만들고자 하는 프로젝트는 동시성을 제공해야 하며 데이터 유실을 최대한 막고 싶었고 그래서 선택하게 된 방식이 Write-Through 였다.

하지만 Write-Around 방식도 있고, 이 경우에는 대용량 서비스 처리에서는 유실가능성을 고려해야한다. RDBMS Data가 최신이 아닐 수 있기 때문에 RDBMS Data를 in-memory 로 정기적으로 Pushing 처리해주는 방식을 같이 사용된다.

Caching 전략에 따라 불필요한 캐쉬 데이터를 가지고 있는 건 메모리 낭비로 이어질 수 있기 때문에 오히려 성능을 떨어뜨릴 수 있다. 

 

따라서 적절한 TTL처리와 성능 개선에 필요한 주요데이터(UPDATE가 많이 발생하지 않는)만을 @RedisCache 처리해서 사용했다. 이번 프로젝트에서는 Program, ProgramDetail, Categories 처럼 update가 많지 않을 것으로 예상되는 항목만에만 적용했다. 

댓글