티스토리 뷰
목표
- MySQL 또는 MariaDB의 레플리케이션(1개 마스터, n개 슬레이브)를 지원하는 하나의
DataSource
빈을 생성한다. - 앞서 생성한 DataSource 빈을 관리하는
PlatformTransactionManager
빈을 생성한다. - 앞서 생성한 DataSource 빈에 연결하는 MyBatis의
SqlSession
빈을 생성한다.
기대효과
- 개발자가 직접 각 데이터베이스를 직접 바라보는 로우 레벨의 DataSource 구성을 관리할 필요 없이 평소와 동일하게 비즈니스 로직에 집중할 수 있다.
@Service
클래스의 각 메써드 레벨에 명시하는@Transactional
어노테이션으로 마스터, 슬레이브로 향할 SQL 문을 결정할 수 있다. @Transactional(readOnly = true)이면 슬레이브로, @Transactional(readOnly = false)이면 마스터로 쿼리를 실행한다.
사전지식
application.yml
/src/main/resources/application.yml 파일에 아래 내용을 추가한다. 예시일 뿐 운영 환경의 자세한 설정은 관련 링크를 참고한다. [관련 링크1] [관련 링크2]
spring:
master:
datasource:
type: org.apache.tomcat.jdbc.pool.DataSource
driverClassName: net.sf.log4jdbc.DriverSpy
url: jdbc:log4jdbc:mariadb://your-master-db-domain:3306/your-db
username: username
password: password
testOnBorrow: true
validationInterval: 34000
validationQuery: SELECT 1
slave:
datasource:
type: org.apache.tomcat.jdbc.pool.DataSource
driverClassName: net.sf.log4jdbc.DriverSpy
url: jdbc:log4jdbc:mariadb://your-slave-db-domain:3306/your-db
username: username
password: password
testOnBorrow: true
validationInterval: 34000
validationQuery: SELECT 1
testOnBorrow
: 운영 환경에서 필수 설정이다. 기본 값은 false로 true로 설정하면 커넥션 풀에서 커넥션을 빌릴 때 마다 유효한 커넥션인지 검사한다. 끊긴 커넥션일 경우 커넥션 풀에서 해당 커넥션을 버리도록 한다. 이 옵션은 혼자 만으로는 작동할 수 없으며 아래 설명할validationInterval
,validationQuery
설정을 필요로 한다.validationInterval
: 앞서 커넥션 풀에서 빌리는 최초 시점에만 유효한 커넥션인지 검사하는 것보다 훨씬 적극적으로 설정된 시간마다 유효 커넥션 여부를 검사한다. 기본 값은 3000(3초)로 운영 환경에서는 34000(34초)을 권장한다. MySQL(또는 MariaDB) 상에서는 반대로wait_timeout
(SELECT @@wait_timeout) 값에 정의된 시간만큼 사용되지 않는 커넥션을 해제하므로 실제 데이터베이스 설정 값을 고려하여 설정해야 한다. 당연히 주기가 짧아질수록 빈번한 조회로 성능은 하락한다.validationQuery
: 유효 커넥션 여부를 실제 검증하는 쿼리문을 작성한다. 가장 부하가 적은 SELECT 1로 설정한다.
DataSourceConfig
- 아래와 같이 @Configuration 클래스를 작성하면 모든 준비가 완료된다. 기본 프로젝트 구성은 이 글을 참고한다.
package com.jsonobject.example.config;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mybatis.replication.datasource.ReplicationRoutingDataSource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
public class DataSourceConfig {
@Primary
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.master.datasource")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.slave.datasource")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "routingDataSource")
public DataSource routingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slaveDataSource") DataSource slaveDataSource) {
ReplicationRoutingDataSource routingDataSource = new ReplicationRoutingDataSource(masterDataSource, null);
routingDataSource.addSlave(slaveDataSource);
return routingDataSource;
}
@Bean(name = "dataSource")
public DataSource dataSource(@Qualifier("routingDataSource") DataSource routingDataSource) {
return new LazyConnectionDataSourceProxy(routingDataSource);
}
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource, ApplicationContext applicationContext) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources("classpath:mapper/*.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean(name = "sqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
참고 글
댓글
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 평속
- Spring MVC 3
- kotlin
- Tomcat
- jpa
- 태그를 입력해 주세요.
- Docker
- chrome
- 알뜰폰
- Kendo UI Web Grid
- Kendo UI
- 로드바이크
- DynamoDB
- 구동계
- Eclipse
- node.js
- MySQL
- jstl
- maven
- bootstrap
- java
- JHipster
- 자전거
- graylog
- jsp
- CentOS
- Spring Boot
- JavaScript
- spring
- 로드 바이크
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
글 보관함