본문 바로가기
프로젝트

파이널 프로젝트_ MySQL 데이터베이스 이중화 - 읽기 분리 전략 적용하기

by 유서담 2024. 5. 7.

참고한 블로그

https://velog.io/@skyjoon34/MySQL-RDS-%EC%9D%BD%EA%B8%B0%EC%A0%84%EC%9A%A9-replica-%EC%A0%81%EC%9A%A9

 

MySQL RDS 읽기전용 replica 적용

간단한 읽기 전용 replica

velog.io

 

 


 

DB 생성부분은 빼고 Spring 적용부분부터 진행

 

 

Spring 프로젝트에 적용

 

 

DataSourceConfiguration

@Slf4j
@Configuration
public class DataSourceConfiguration {

    private static final String MASTER_SERVER = "MASTER"; // 마스터 서버를 나타내는 상수 문자열입니다.
    private static final String REPLICA_SERVER = "REPLICA"; // 복제 서버를 나타내는 상수 문자열입니다.

    @Bean
    @Qualifier(MASTER_SERVER)
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        // 'spring.datasource.master'로 시작하는 구성 속성을 사용하여 DataSource를 작성합니다.
        return DataSourceBuilder.create() 
                .build();
    }

    @Bean
    @Qualifier(REPLICA_SERVER)
    @ConfigurationProperties(prefix = "spring.datasource.replica")
    public DataSource replicaDataSource() {
    	// 'spring.datasource.replica'로 시작하는 구성 속성을 사용하여 DataSource를 작성합니다.
        return DataSourceBuilder.create() 
                .build();
    }

    @Bean
    public DataSource routingDataSource(
            @Qualifier(MASTER_SERVER) DataSource masterDataSource,
            @Qualifier(REPLICA_SERVER) DataSource replicaDataSource
    ) {
    	// 사용자 정의 RoutingDataSource 인스턴스를 생성합니다.
        RoutingDataSource routingDataSource = new RoutingDataSource(); 

	// 데이터 소스를 보관할 맵을 생성합니다.
        HashMap<Object, Object> dataSourceMap = new HashMap<>();
        
        // "master" 키로 마스터 DataSource를 맵에 넣습니다.
        dataSourceMap.put("master", masterDataSource);
        
        // "replica" 키로 복제 DataSource를 맵에 넣습니다.
        dataSourceMap.put("replica", replicaDataSource); 
		
        // 라우팅을 위한 대상 데이터 소스를 설정합니다.
        routingDataSource.setTargetDataSources(dataSourceMap); 
        
        // 기본 대상 데이터 소스를 마스터 DataSource로 설정합니다.
        routingDataSource.setDefaultTargetDataSource(masterDataSource); 

	// routingDataSource를 반환합니다.
        return routingDataSource; 
    }

    @Bean
    @Primary
    public DataSource dataSource() {
        // 라우팅 로직을 기반으로 적절한 DataSource를 결정합니다.
        DataSource determinedDataSource = routingDataSource(masterDataSource(), replicaDataSource()); 
        
        // 지연 연결 DataSource 프록시를 반환합니다.
        return new LazyConnectionDataSourceProxy(determinedDataSource); 
    }

}

 

 

 

RoutingDataSource

@Slf4j
public class RoutingDataSource extends AbstractRoutingDataSource {
    @Nullable
    @Override
    protected Object determineCurrentLookupKey() {
        String lookupKey = TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? "replica" : "master";
        log.info("Current DataSource is {}", lookupKey);

        return lookupKey;
    }
}

 

위 DataSourceConfiguration 에서 만든 RoutingDataSource를 정의하는 클래스

AbstractRoutingDataSource 를 추상화하고 있다

 

isCurrentTransactionReadOnly 이 메서드로 트랜젝션이 readOnly=true일 때는 replica를 반환

 

 

 

application.properties

 

 

 

메소드에 적용하기

 

메소드에 적용 전 

 

메소드 적용 전에 실행하면 Current DataSource is master 라고 나온다

 

 

메소드에 적용 후

 

값을 읽어오는 서비스에 

@Transactional(readOnly = true) 애노테이션을 적용시켰다

 

 

적용시킨 후에는 

Current DataSource is replica 라고 출력이 된다

 


 

MySQL RDS 읽기 전용 복제를 설정하면 좋은 점

 

읽기 부하 분산

데이터베이스에 정보를 요청하는 일을 복제 데이터베이스가 나눠서 처리함으로써, 주 데이터베이스에 가해지는 부담을 줄일 수 있다. 이렇게하면 주 데이터베이스가 다른 중요한 일에 더 집중할 수 있다

 

가용성 향상

만약 주 데이터베이스에 문제가 생기더라도 읽기 전용의 복제 데이터베이스가 데이터를 제공할 수 있다

 

읽기 성능 향상

데이터를 요청하는 일을 복제 데이터베이스가 처리하게 함으로써, 더 빠르게 처리할 수 있다

 

백업 및 복구 용이성

데이터베이스 복제본을 만들어두면, 주 데이터베이스에 문제가 생겼을 때 빠르게 원래대로 복구할 수 있다