개발/Spring

Spring boot Data JPA Hikari Master/Slave (Write,Read Only) 사용하기.

JangHC 2022. 4. 6. 21:34

DATA JPA를 사용할 때, read only와 write db를 따로 구성해서 서버 성능을 올리고 싶을때 사용한다.

 

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

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.jdbc.datasource.LazyConnectionDataSourceProxy;

import com.zaxxer.hikari.HikariDataSource;

@Configuration
public class MyDataSourceConfiguration  {
    public static final String MASTER_DATASOURCE = "masterDataSource";
    public static final String SLAVE_DATASOURCE = "slaveDataSource";

    @Bean(MASTER_DATASOURCE) 
    @ConfigurationProperties(prefix = "spring.datasource.master.hikari")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create() 
            .type(HikariDataSource.class)
            .build();
    }

    @Bean(SLAVE_DATASOURCE)
    @ConfigurationProperties(prefix = "spring.datasource.slave.hikari")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create()
            .type(HikariDataSource.class)
            .build();
    }
    
    @Bean
    @Primary
    @DependsOn({MASTER_DATASOURCE, SLAVE_DATASOURCE})
    public DataSource routingDataSource(
        @Qualifier(MASTER_DATASOURCE) DataSource masterDataSource,
        @Qualifier(SLAVE_DATASOURCE) DataSource slaveDataSource) {

        RoutingDataSource routingDataSource = new RoutingDataSource();

        Map<Object, Object> datasourceMap = new HashMap<>() {
            {
                put("master", masterDataSource);
                put("slave", slaveDataSource);
            }
        };

        routingDataSource.setTargetDataSources(datasourceMap);
        routingDataSource.setDefaultTargetDataSource(masterDataSource);

        return routingDataSource;
    }
    
    @Bean
    @DependsOn("routingDataSource")
    public LazyConnectionDataSourceProxy dataSource(DataSource routingDataSource){
        return new LazyConnectionDataSourceProxy(routingDataSource);
    }
}

 

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.support.TransactionSynchronizationManager;

public class RoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() { // (1)
        return (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) ? "slave" : "master"; //(2)
    }
}

 

properties

 

#DB
spring.datasource.master.hikari.username=
spring.datasource.master.hikari.password=
spring.datasource.master.hikari.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.master.hikari.jdbc-url=
spring.datasource.master.hikari.minimum-idle=5
spring.datasource.master.hikari.connection-timeout=30000
spring.datasource.master.hikari.validation-timeout=30000
spring.datasource.master.hikari.max-lifetime=240000
spring.datasource.master.hikari.maximum-pool-size=5

spring.datasource.slave.hikari.username=
spring.datasource.slave.hikari.password=
spring.datasource.slave.hikari.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.slave.hikari.jdbc-url=
spring.datasource.slave.hikari.minimum-idle=5
spring.datasource.slave.hikari.connection-timeout=30000
spring.datasource.slave.hikari.validation-timeout=30000
spring.datasource.slave.hikari.max-lifetime=240000
spring.datasource.slave.hikari.maximum-pool-size=5

 

 

	private final MemberRepository memberRepository;
    @Transactional(readOnly = true)
    public Member selectMember(String memberKey){
        return memberRepository.findByMemberKey(memberKey);
    }

이런식으로 select만 하는 method를 만들어 사용하면 된다.

 

주의할 점은 readOnly가 설정되어 있더라도 method 안에 write하는 쿼리가 있다면, method 안에 모든 쿼리는 write db로 쓰여진다.