실제 현업에서는 Master, Slave로 이중화를 기본으로 세팅되어 있는걸 많이 보았다. Master는 Write용도, Slave는 Read용도로 써서 트래픽을 분산 처리하는 장점정도는 알고있지만, 상세한 내용에 대해서는 알지 못했다.
그래서 기존 토이 프로젝트에 MySQL Replication(복제) 공부도 할 겸 Master, Slave로 이중화 작업을 시도해보았다.
<Replication 기본 개념>
일단 MySQL Replication의 동작 원리에 대해서 우선 살펴보자.
MySQL의 Replication은 비동기 복제 방식을 사용하고있다.
- Master에서 변경이 있으면 이를 바이너리 로그에 저장하고 이를 비동기적으로 Slave에게 전송한다.
- Slave에서는 이를 받아 릴레이 로그에 저장한 후 변경사항을 스토리지 엔진에 반영한다.
cloudrain21.com/mysql-replication
더 자세한 내용은 위 블로그 참조!!
핵심은 Master에게는 데이터 동시성이 아주 높게 요구되는 트랜잭션을 담당하고, Slave에게는 데이터 동시성이 꼭 보장될 필요 없는 경우에 읽기 전용으로 데이터를 가져오게 된다는 것이다.
<Replication 실습 간단 요약>
- Docker를 활용해 MySQL 컨테이너를 Master, Slave 2대를 띄운다. 간편하게 docker-compose를 활용해 한번에 생성하는게 나은듯
- Slave 서버에서는 Master의 데이터를 읽어오기 위한 ip 등의 정보를 설정해줘야 한다.
- 예시
CHANGE MASTER TO
MASTER_HOST='{master network ip address}',
MASTER_USER='root',
MASTER_PASSWORD='password',
MASTER_LOG_FILE='mysql-bin.000001',
...
- 연동이 정상적으로 되었다면 Master 서버에 데이터를 Write 했을때, Slave 서버에서도 동일하게 반영되는걸 확인할 수 있다.
<SpringBoot Master, Slave Transaction 분리>
@Transactional(readOnly = true)
public List<Board> getBoard() {
return boardRepository.findAll();
}
@Transactional
public void insertBoard(Board board) {
boardRepository.save(board);
}
@Transactional readOnly 값에 따라 master, slave 라우팅이 정해진다.
true이면 slave db, false이면 master db로 요청이 전해진다. 저 옵션 기준으로 처리하기 위해서는 추가적인 설정 작업이 필요할것같지 않은가? 일단은 예상만 해보고 하나씩 적용해보자
1. master, slave로 db가 분리되어 있으니 application.yml에 datasource를 각각 분리해서 커넥션 생성을 한다.
datasource:
master:
hikari:
...
slave:
hikari:
...
2. spring 기본 커넥션풀인 tomcat-jdbc가 아닌 hikaricp를 활용하기 때문에 hikari 관련 bean을 직접 생성해줘야한다. 실제 yml 정보가 매핑되는 필드는 HikariDataSource가 상속하고 있는 HikariConfig에 존재한다.
@Configuration
public class DataSourceConfig {
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();
}
...
}
3. 위에서 언급했던 transactional readonly 옵션으로 라우팅 처리하기 위한 설정이다.
@Configuration
public class RoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) ? "slave" : "master";
}
}
핵심 코드는 이정도이다. 서버 띄워서 write, read 메소드 각각 실행해보면 라우팅이 정상적으로 잘 처리되는걸 볼수있다.
실제로 라우팅 되는지 로그를 보고싶다면
general_log 값을 OFF => ON 으로 수정하면 실시간 로그를 모니터링 할 수 있다. 해당 내용은 log_file에서 확인 가능하다.
참고 링크
https://mytalkhome.tistory.com/840
https://huisam.tistory.com/entry/mysql-replication
https://velog.io/@ililil9482/Spring-Boot-MySqlMaster-Slave-%EA%B5%AC%EC%84%B1
https://k3068.tistory.com/102#AbstractRoutingDataSource%--%EA%B-%AC%ED%--%--
https://tjdrnr05571.tistory.com/14
'백엔드 > Spring' 카테고리의 다른 글
멀티 서버 구동시 스케줄러 중복 실행 방지하기 (0) | 2022.07.10 |
---|---|
Spring WebFlux를 이용한 100만건 엑셀 파일 처리 테스트 (5) | 2021.03.26 |
쿠키, 세션이란? (0) | 2021.01.24 |
JPA란? (0) | 2020.06.08 |
Ajax - @RequestBody @ResponseBody에 대해.. (0) | 2019.12.16 |