์ด ํฌ์คํ
์ ์ง๋ ํฌ์คํ
์ธ DB ๋ค์คํ์ Replication(MySQL ํธ) ์ ์ด์ด์ ์์ฑ๋์์ต๋๋ค.
DB ์๋ฒ๋ฅผย Master - Slaveย ๋ก ์ด์คํ ํ์์ผ๋ฏ๋ก,ย SpringBoot์์ ์ฌ์ฉํ๋ย DataSource๋ย Master - Slave๋ฅผ ๊ฐ๊ฐ ์ฌ์ฉํด์ผ ํฉ๋๋ค. ์ด๋ฅผ ๊ตฌํํ๊ธฐ ์ํ ๋ฐฉ๋ฒ์ผ๋กย @Transactionalย ์ ๋
ธํ
์ด์
์ ์์ฑ์ ์ด์ฉํ์ฌย readOnly = false์ธ ํธ๋์ญ์
์ย Master DataSource๋ฅผ,ย readOnly = true์ธ ํธ๋์ญ์
์ย Slave DataSource๋ฅผ ์ฌ์ฉํ๋๋ก ์ค์ ํด ์ฃผ๊ฒ ์ต๋๋ค.
๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ฒ๋ฅผ Source - Replica ๋ก ์ด์คํ ํ์์ผ๋ฏ๋ก, ์คํ๋ง๋ถํธ์์ ์ฌ์ฉํ๋ DataSource๋ Source - Replica์ ๋ง๊ฒ 2๊ฐ๋ฅผ ์จ์ผํ๋ค.ย readOnly = trueย ํธ๋์ญ์
์ Replica DataSource๋ฅผ,ย readOnly = falseย ์ธ ํธ๋์ญ์
์ Source DataSource๋ฅผ ์ฌ์ฉํ๋๋ก ๋ถ๊ธฐํด์ผํ๋ค.
application.yml ์ค์
์ ๋ย Master 1,ย Slave 2ย ๋ฅผ ๋ฐํ์ผ๋กย Replication์ ๊ตฌ์ฑํ๊ธฐ ๋๋ฌธ์ 3๊ฐ์ย DataSource์ ๋ํ ์ค์ ์ ์์ฑํด์ฃผ์ด์ผ ํฉ๋๋ค. ์ฌ๊ธฐ์ ํ๋ ์ ์ํด์ผ ํ ์ ์ ์ผ๋ฐ์ ์ธย DataSourceย ๋ฑ๋ก ๋ฐฉ๋ฒ๊ณผ๋ ๊ณผ์ ์ด ์กฐ๊ธ ๋ฌ๋ผ์ง๋ค๋ ์ ์
๋๋ค.
ํ๋์ย DataSource๋ง ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ย SpringBoot AutoConfiguration์ ํตํด์ ์๋์ผ๋ก ๋น์ผ๋ก ๋ง๋ค์ด์ ธ ๊ด๋ฆฌ๋์์ง๋ง, 2๊ฐ ์ด์์ย DataSource๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ ๊ฐ๋ฐ์๊ฐ ์ง์ ๋น์ ๋ง๋ค์ด์ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
์ผ๋จ 2๊ฐ์ DataSource์ ๋ํ ์ค์ ์ย application.ymlย ์ ์ ์ด์ค ๊ฒ์ด๋ค. ํ๋์ DataSource์ ๋ํด์๋ ์คํ๋ง๋ถํธ๊ฐ ์๋์ผ๋ก ๋น์ผ๋ก ๋ง๋ค์ด ๊ด๋ฆฌํด์คฌ์ง๋ง, 2๊ฐ ์ด์์ DataSource๋ฅผ ์ฌ์ฉํ ๋์๋ ์ฐ๋ฆฌ๊ฐ ์ง์ ๋น์ ๋ง๋ค์ด ์ฌ์ฉํด์ผํ๋ค. ์ผ๋จ ์๋์ ๊ฐ์ด ์ค์ ํ์.
spring:
datasource:
source:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: "jdbc:mysql://localhost:13306/morak"
username: root
password: root
replica1:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: "jdbc:mysql://localhost:13307/morak"
username: root
password: root
replica2:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: "jdbc:mysql://localhost:13308/morak"
username: root
password: root
jpa:
show-sql: true
YAML
๋ณต์ฌ
yml ์ ๋ณด ๊ฐ์ฒด ๋ฐ์ธ๋ฉ
์์ yml์ ์ค์ ํ ์ ๋ณด๋ ์ฌ์ฉ์ ์์๋ก ์ ์ํด์ค ํ์์ด๊ธฐ ๋๋ฌธ์ ํด๋น ์ ๋ณด๋ฅผ java ์ฝ๋๋ก ์ฌ์ฉํ ์ ์๋๋กย ConfigurationProperties๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ธ๋ฉํด์ฃผ์ด์ผํฉ๋๋ค. ํด๋น ์ด๋
ธํ
์ด์
์ ๋ด๋ถ์ ์ผ๋ก getter, setter๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ํ์์ ์ผ๋ก getter, setter ๋ฉ์๋๊ฐ ์์ด์ผํฉ๋๋ค.
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.source")
public DataSource sourceDataSource() {
return DataSourceBuilder.create()
.build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.replica1")
public DataSource replica1DataSource() {
return DataSourceBuilder.create()
.build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.replica2")
public DataSource replica2DataSource() {
return DataSourceBuilder.create()
.build();
}
}
Java
๋ณต์ฌ
์๋์ผ๋ก Datasource๋ฅผ ๋ฑ๋กํด์ฃผ์๋ ์ด์ ๊ณผ๋ ๋ค๋ฅด๊ฒ ๋ฑ๋กํด์ค์ผํ Datasource๊ฐ ์ฌ๋ฌ๊ฐ์ด๋ฏ๋ก ์๋์ผ๋ก ๋ฑ๋กํด์ฃผ์ด์ผํฉ๋๋ค.
๋ค์๊ณผ ๊ฐ์ด ๊ฐ DB ์๋ฒ์ ๋์๋๋ย DataSourceย ํ์
์ ๋น์ ๋ฑ๋กํ๋ฉด ๋ฉ๋๋ค.
์ถ๊ฐ์ ์ผ๋กย @ConfigurationPropertiesย ****์ ๋
ธํ
์ด์
์ ์ฌ์ฉํ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค. ํด๋น ์ ๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ฉดย application.yml์ ๋ช
์ํ ์ฌ๋ฌ ์ค์ ์ค, ํน์ ย prefix์ ํด๋นํ๋ ์ค์ ๊ฐ์ ์๋ฐ ๋น์ ๋งคํํ ์๊ฐ ์์ต๋๋ค.
Source Replica ๋ถ๊ธฐ์ฒ๋ฆฌ
์คํ๋ง์ย Multi DataSourceย ํ๊ฒฝ์์ ์ฌ๋ฌย DataSource๋ฅผ ๋ฌถ๊ณ ๋ถ๊ธฐํด์ฃผ๊ธฐ ์ํด์ย AbstractRoutingDataSource๋ผ๋ ์ถ์ ํด๋์ค๋ฅผ ์ง์ํฉ๋๋ค.
public class RoutingDataSource extends AbstractRoutingDataSource {
private final RoutingReplicas<String> routingReplicas;
public RoutingDataSource(List<String> routingReplicas) {
this.routingReplicas = new RoutingReplicas<>(routingReplicas);
}
@Override
protected Object determineCurrentLookupKey() {
boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
if (isReadOnly) {
return routingReplicas.get();
} else {
return "source";
}
}
}
Java
๋ณต์ฌ
์ค์ ์ ํฌ๊ฐ ์ด๋ฅผ ํ์ฉํ๊ธฐ ์ํด์ย AbstractRoutingDataSourceย ๋ฅผ ์์๋ฐ๋ ๊ตฌ์ฒด ํด๋์ค๋ฅผ ๋ง๋ค์ด์ ์์์ ์ธ๊ธํย determineCurrentLookupKey()๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ์ฌ ํธ๋์ญ์
์ย readOnlyย ๊ฐ์ ๋ฐ๋ผ ๋ค๋ฅธย DataSourceย ์ย Key๋ฅผ ๋ฐํํ๋๋ก ๊ตฌํํ๊ฒ ์ต๋๋ค.
@Transactional(readOnly=true)์ธ ๊ฒฝ์ฐ replica datasource๋ก, ๋๋จธ์ง๋ source datasource๋ก ๋ถ๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํ๊ธฐ์ํ ReplicationRoutingDataSource ํด๋์ค๋ฅผ ์์ฑํฉ๋๋ค.
determineCurrentLookupKey()๋ฉ์๋๋ ํ์ฌ ์์ฒญ์์ ์ฌ์ฉํ datasource์ key๊ฐ์ ๋ฐํํด์ค๋๋ค.
get์์ ์ด๋ค replica๋ฅผ ์ฌ์ฉํ ์ง ์ ํด์ง๋๋ค.
public class RoutingReplicas<T> {
private final List<T> replicas;
private final AtomicInteger index;
public RoutingReplicas(List<T> replicas) {
this.replicas = replicas;
index = new AtomicInteger(0);
}
public T get() {
return replicas.get(index.getAndIncrement() % replicas.size());
}
}
Java
๋ณต์ฌ
๋ค์๊ณผ ๊ฐ์ดย RoutingCircularย ํด๋์ค๋ฅผ ๋ง๋ค์ด ํตํด์ย ์ฌ๋ฌ๊ฐ์ย Slave DB์ย DataSource๋ฅผ ์์๋๋ก ๋ก๋๋ฐธ๋ฐ์ฑย ํ ์ ์๊ฒ ๋ฉ๋๋ค.
DataSourceConfig ๊ตฌํ
@Configuration
public class DataSourceConfig {
// data sources
@Bean
public DataSource routingDataSource(
DataSource sourceDataSource,
DataSource replica1DataSource,
DataSource replica2DataSource
) {
Map<Object, Object> dataSources = new HashMap<>();
dataSources.put("source", sourceDataSource);
dataSources.put("replica1", replica1DataSource);
dataSources.put("replica2", replica2DataSource);
RoutingDataSource routingDataSource = new RoutingDataSource(List.of("replica1", "replica2"));
routingDataSource.setDefaultTargetDataSource(dataSources.get("source"));
routingDataSource.setTargetDataSources(dataSources);
return routingDataSource;
}
@Primary
@Bean
public DataSource dataSource() {
return new LazyConnectionDataSourceProxy(routingDataSource(sourceDataSource(), replica1DataSource(), replica2DataSource()));
}
}
Java
๋ณต์ฌ
์์์ ๋ง๋ ย RoutingDataSourceย ์ ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ Source DataSource์ Replica DataSource ์ ๋ณด๋ฅผ ๋ฑ๋กํ๊ณ , ์ด๋ฅผ ๋น์ผ๋ก ๋ง๋ค๊ฒ์ด๋ค. ์๋ ์์ค์ฝ๋๋ย sourceDataSourceย ์ย replicaDataSourceย ๋น์ ์ ์ํย DataSourceConfigurationย ํด๋์ค์ ์์ค์ฝ๋์ ์ด์ด ์์ฑํ ๋ด์ฉ์ด๋ค.
AOP๋?
SET AUTO COMMIT ์?
LazyConnectionDataSourceProxy๋ฅผ ์ฌ์ฉํ๋ ์ด์
์ผ๋ฐ์ ์ผ๋ก Spring์์๋ DataSource๋ฅผ ํ๋๋ง ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์์์ ์ ์ ํด ๋์ต๋๋ค. ๊ทธ๋ฌ๋ ๋ฆฌํ๋ ์
์ ์ฌ์ฉํ ๊ฒฝ์ฐ DataSource๊ฐ ์์ง ์ ํด์ง์ง ์์์ผ๋ฏ๋ก Lazy ์ ๋ต์ ์ฌ์ฉํ์ฌ ์ค์ ์ฟผ๋ฆฌ๊ฐ ์คํ ๋ ๋ DataSource๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
์ด์ ์ค์ ์คํ๋ง๋ถํธ ์ ๋ฐ์์ ์ฌ์ฉ๋ ย DataSource dataSource()ย ๋น์ ๋ง๋ค์ด๋ณด์. ๊ทธ๋ฐ๋ฐ, ์คํ๋ง์ดย DataSourceย ๋ฅผ ํตํดย Connectionย ์ ํ๋ํ๋ ๊ณผ์ ์ ๋ํด ์ดํดํ ํ์๊ฐ ์๋ค.
์คํ๋ง์ ํธ๋์ญ์
์ ์ง์
๋ ์๊ฐ ๋ฐ๋กย DataSourceย ๋ฅผ ๊ฐ์ ธ์ค๊ณ , ์ปค๋ฅ์
์ ํ๋ํ๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ๋ค์์ ํธ๋์ญ์
์ ํ์ฌ ์ํ๊ฐ ์ ์ฅ๋๋ค. ์ฆ, TransactionSynchronizationManager์ ํธ๋์ญ์
์ ๋ณด๋ฅผ ๋๊ธฐํ ํ๋ ์์
์ DataSource๋ก๋ถํฐ Connection์ ์ป์ด์จ ์ดํ ๋์ํ๋ค.
๋ฐ๋ผ์ย LazyConnectionDataSourceProxyย ๋ผ๋ ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ํธ๋์ญ์
์ ์ง์
ํ ์์ ์ด ์๋๋ผ, ์ค์ ์ฟผ๋ฆฌ๊ฐ ์์๋ ์์ ์ย DataSourceย ๊ฐ ์ ํ๋๋๋ก ์ง์ฐ(lazy) ์ํฌ ํ์๊ฐ ์๋ค.
๊ฐ์ ์
โข
์ปค๋ฅ์
๋ฌธ์
โข
์ถ๊ฐ ์ ๊ฑฐ ๋ฌธ์