[ Spring Multi DataSource ] Read / Write Dynamic DataSource
AWS ์ฌ์ฉ ์ค, READ ์ READ/WRITE DB๊ฐ ๋ถ๋ฆฌ๋๋ฉด์, ํ์ฌ ์ฌ์ฉ์ค์ธ 1๊ฐ์ DataSource ๋ฅผ ๋ถ๋ฆฌํ์ฌ READ์ ์ฉ ds ์ READ/WRITE ์ ์ฉ ds ๋ฅผ ๊ตฌ๋ถํ์ฌ ์ฌ์ฉํด์ผ ํ๋ ์ํฉ์ด ๋์๋ค.
๊ตฌ๊ธ๋ง์ ํ๋ฉด์ ๋ค์๊ณผ ๊ฐ์ ๊ธ์ ์ฐธ๊ณ ํ์ฌ ์์ฑํ์๋ค.
https://taes-k.github.io/2020/03/11/sprinig-master-slave-dynamic-routing-datasource/
Spring, master-slave dynamic routing datasource ์ฌ์ฉํ๊ธฐ
DB Replication ์๋น์ค๋ฅผ ์ด์ํ๋ฉด์ DB์ ๋ถํ๋ฅผ ์ค์ด๊ธฐ ์ํ ๋ฐฉ๋ฒ์ผ๋ก DB Replication์ ํตํด ์ฟผ๋ฆฌ์ ๋๋ถ๋ถ์ ์ฐจ์งํ๋ read ์์ ์ Slave DB๋ฅผ ์ฌ์ฉํ๊ฒ๋ ํ์ฌ ๋ถํ๋ฅผ ๋ถ์ฐ ์ํฌ์ ์์ต๋๋ค. Read์ ๋ถํ
taes-k.github.io
https://mudchobo.github.io/posts/spring-boot-jpa-master-slave
Spring Boot JPA - master slave ๋ถ๊ธฐ ์ฒ๋ฆฌ - transactional ๋ฐฉ์ - mudchobo devlog
์คํ๋ง ๋ถํธ์์ JPA๋ ๊ธฐ๋ณธ ์ ํ ์ 1๊ฐ์ datasource๋ง ์ค์ ํ๊ฒ ๋์ด ์๋ค. ํ์ง๋ง, master / slave replication์ด ๋์ด ์๋ ๋๋น๋ฅผ ๋ ๋ค ์ฐ๊ฒฐํ๊ณ ์ถ์ ๋์๋ ์กฐ๊ธ ๊น๋ค๋กญ๊ฒ ์ค์ ํด์ผ ํ๋ค. ๋ ๊ฐ์ง ๋ฐฉ
mudchobo.github.io
1. SQLConfig.java
package com.test;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
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.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@MapperScan(basePackages = "com.test.repo.postgreSQL")
@EnableTransactionManagement
public class PostgreSQLConfig {
@Bean
@ConfigurationProperties("spring.datasource.hikari.master")
public DataSource dataSourceMaster() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@Bean
@ConfigurationProperties("spring.datasource.hikari.slave")
public DataSource dataSourceSlave() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@DependsOn({"dataSourceMaster","dataSourceSlave"})
@Bean
public DataSource routingDataSource(@Qualifier("dataSourceMaster") DataSource dataSourceMaster,
@Qualifier("dataSourceSlave") DataSource dataSourceSlave) {
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("master", dataSourceMaster);
dataSourceMap.put("slave", dataSourceSlave);
PostgreSQLRoutingDataSource routingDataSource = new PostgreSQLRoutingDataSource();
routingDataSource.setTargetDataSources(dataSourceMap);
routingDataSource.setDefaultTargetDataSource(dataSourceMaster);
return routingDataSource;
}
@Primary
@DependsOn({"routingDataSource"})
@Bean
public DataSource dataSource(@Qualifier("routingDataSource") DataSource routingDataSource) {
return new LazyConnectionDataSourceProxy(routingDataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("routingDataSource") DataSource dataSource) throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sessionFactory.setMapperLocations(resolver.getResources("classpath:mybatis/mapper/postgreSQL/*.xml"));
Resource myBatisConfig = new PathMatchingResourcePatternResolver()
.getResource("classpath:mybatis/config/mybatis-config.xml");
sessionFactory.setConfigLocation(myBatisConfig);
return sessionFactory.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) throws Exception {
final SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory);
return sqlSessionTemplate;
}
}
์์ฑํ๋ค๊ฐ ์ด๋ ธํ ์ด์ ์ ๋ํ ๊ฒ๋ ์๋ก์ด ์๊ฒ๋์๋ค.
- Qualifier : ๋์ผํ ํ์ ์ ๊ฐ๋ ๋น ๊ฐ์ฒด๋ฅผ ์ฃผ์ ์, ์คํ๋ง์์๋ ์ ์ ์๊ธฐ ๋๋ฌธ์ ๋ช ์ํด์ฃผ๋ ๊ฒ.
- DependsOn : ์คํ๋ง ์ํ ์ค๋ฅ ๋ฐ์ ์, ์์กด์ฑ ์์๋ฅผ ์ง์ ํ์ฌ ์ํ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง ์๋๋ก ๋ช ์, DependsOn ๋ค์ ์ค๋ Name ์ ๊ฐ๋ ๋น ์ฃผ์ ํ, ๋ค์ ์์๋ก ์ฃผ์ ๋๊ฒ๋ ํจ.
2. RoutingDataSource.java
package com.test.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class PostgreSQLRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
if (isReadOnly) {
System.out.println("๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ");
System.out.println("๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ DB Connection now is - slave - ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ");
System.out.println("๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ");
return "slave";
} else {
System.out.println("๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ");
System.out.println("๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ DB Connection now is - master - ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ");
System.out.println("๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ๏ผ");
return "master";
}
}
}
3. application.yml
...
datasource:
hikari:
master:
jdbc-url: jdbc:log4jdbc:postgresql://{url}
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
username: master
password: master
slave:
jdbc-url: jdbc:log4jdbc:postgresql://{url}
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
username: slave
password: slave
...
๊ฐ๋จํ๊ฒ ์ค๋ช ํ์๋ฉด, Service ์ ์ ์ธ๋ @Transactional ํ๊ทธ์ readOnly ๊ฐ true ๋ false ๋ ์ ๋ฐ๋ผ ์ ์ฉ๋๋ ds๊ฐ ๋ฌ๋ผ์ง๋ ํํ์ด๋ค.
@Transactional
or
@Transactional(readOnly = true)
๋ฏธ๋ฆฌ ์์ฑ๋ master ์ slave ds ๊ฐ์ฒด๋ฅผ ๋งต ํํ๋ก ๊ฐ์ง๊ณ ์๋ค๊ฐ, ํธ์ถ ์ readOnly ์ฌ๋ถ๋ฅผ ํ๋จํ์ฌ ์ ์ ํ ds ๋ฅผ ๋ฆฌํดํ์ฌ sqlSession์ ๊ตฌ์ฑํ๊ฒ ๋๋ค.
* DependsOn ์ ์ ๊ฑฐ ํ ๋น๋ ์, ์คํ๋ง ์ํ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ฌ ์ด๋ฅผ ๋ง๊ธฐ ์ํด ์ถ๊ฐํ์๋ค. ๊ทผ๋ฐ ๋ค๋ฅธ ํ๋ก์ ํธ์ ๋์ผํ๊ฒ ์ ์ฉํ๋ฉด ๋น๋ ์, ์๋ฌ๊ฐ ๋์ง ์์ ํด๋น ๊ตฌ๋ฌธ์ ๊ฐ์ ธ๊ฐ ์ง, ๊ณ ์ณ์ผํ ์ง ๊ณ ๋ฏผ ์ค์ด๋ค.