728x90
DataSource로 DB 이중화를 해볼 것이다.
DB를 Master, Slave 구조로 설계하여
Master DB는 쓰기 (Innsert, Update, Delete) 작업을 수행하고, Slave DB는 읽기(Select) 작업을 수행해서 성능을 향상시키는 방법이다.
build.gradle에 라이브러리 추가
jpa를 추가해주고, mariadb를 사용할 것이기 때문에 mariadb client 라이브러리를 추가해준다.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.mariadb.jdbc:mariadb-java-client'
application.yml에 설정 추가
application에 datasource 설정을 추가해준다
Master DB 서버의 IP 주소, Slave DB 서버의 IP 주소를 적어준다.
spring:
datasource:
master:
jdbc-url: jdbc:mariadb://${MASTER_DB}:3306/web
driver-class-name: org.mariadb.jdbc.Driver
username: ${DB_USER}
password: ${DB_PASSWORD}
slave:
jdbc-url: jdbc:mariadb://${SLAVE_DB}:3306/web
driver-class-name: org.mariadb.jdbc.Driver
username: ${DB_USER}
password: ${DB_PASSWORD}
jpa:
database-platform: org.hibernate.dialect.MariaDBDialect
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
logging:
level:
org.hibernate.SQL: debug
org.hibernate.orm.jdbc.bind: trace
DataSourceConfig
Master-Slave 데이터베이스 이중화 설정을 하는 역할
routingDataSource()
- Master와 Slave DB를 담을 DynamicRoutingDataSource 객체를 생성한다.
- 읽기 작업(SELECT)은 Slave로, 쓰기 작업(INSERT, UPDATE, DELETE)은 Master로 자동 전환한다.
- Master와 Slave를 매핑하고, 기본 DB로 Master를 설정한다.
import com.zaxxer.hikari.HikariDataSource;
import jakarta.persistence.EntityManagerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DataSourceConfig {
// Master DB의 DataSource 생성
@ConfigurationProperties(prefix = "spring.datasource.master")
@Bean
public DataSource masterDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
// Slave DB의 DataSource를 생성
@ConfigurationProperties(prefix = "spring.datasource.slave")
@Bean
public DataSource slaveDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
// Master - Slave 간 전환을 위한 DynamicRoutingDataSource를 생성
@DependsOn({"masterDataSource", "slaveDataSource"})
@Bean
public DataSource routingDataSource(
@Qualifier("masterDataSource") DataSource master,
@Qualifier("slaveDataSource") DataSource slave) {
DynamicRoutingDataSource routingDataSource = new DynamicRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("MASTER", master);
dataSourceMap.put("SLAVE", slave);
routingDataSource.setTargetDataSources(dataSourceMap);
routingDataSource.setDefaultTargetDataSource(master);
return routingDataSource;
}
// 트랜잭션이 시작될 때까지 DB 연결을 지연시켜 성능 최적화
@DependsOn({"routingDataSource"})
@Primary
@Bean
public DataSource dataSource(DataSource routingDataSource) {
return new LazyConnectionDataSourceProxy(routingDataSource);
}
// JPA 트랜잭션 관리자 설정
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory){
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
return jpaTransactionManager;
}
}
DynamicRoutingDataSource
Master-Slave DB를 동적으로 선택하는 역할을 한다.
읽기 전용이면 SLAVE를, 읽기 전용이 아니면 MASTER를 반환한다.
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import static org.springframework.transaction.support.TransactionSynchronizationManager.isCurrentTransactionReadOnly;
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 현재 트랜잭션이 읽기 전용인지 여부를 확인
// 읽기 전용이면 SLAVE, 아니면 MASTER
String dataSourceName = isCurrentTransactionReadOnly() ? "SLAVE" : "MASTER";
System.out.println("dataSourceName : " + dataSourceName);
return dataSourceName;
}
}
728x90
'BE > Spring Boot' 카테고리의 다른 글
[Spring Boot] 환경변수 설정 (IntelliJ에서 설정) (0) | 2025.02.25 |
---|---|
[Spring Boot] 페이지네이션(Pagination) 적용 (0) | 2025.02.25 |
[Spring Boot] Swagger란? / Swagger API 사용하기 (0) | 2025.02.19 |
[Spring Boot] SMTP로 구글 이메일 인증 기능 구현 (회원가입) (0) | 2025.02.19 |
[Spring Boot] Spring Security란? / 인증 수행 과정 / 인가 처리 (1) | 2025.02.10 |