- AWS 자격증을 따면서 왜 RDS, Aroura, DynamoDB를 사용하는지 궁금했다.
- 가장 큰 이유는 Multi AZ와 읽기 전용 복제본이라고 말할 것 같다.
- Multi AZ는 여러 가용영역에 복제본을 만드는 것이다. 따라서 한 가용영역에 문제가 생겨도 다른 지역의 DB로 복구하는 것이다.
- 읽기 전용 복제본은 읽기 기능만 가능한 DB를 만들어 읽기와 쓰기 작업을 하는 DB를 분리하는 것이다.
- DB에서 거의 대부분의 작업이 읽기 작업이다. 읽기랑 쓰기가 같은 DB에서 일어난다고 해보자.
- 만약 DB에 부하가 심해져서 다운되면?? 읽기 전용 복제본을 여러개 만들어서 부하를 분산하는 것이다.
- 처음에 마스터 DB와 슬레이브 DB를 하나의 서버에서 관리했다.
- 이 방식은 트래픽을 분리해줄 지 몰라도 서버 자체에 트래픽이 몰리는 것은 변함이 없다.
- 따라서 더 효율적으로 트래픽을 관리하고 싶다면 서버를 분리하는 것이 맞다.
- 하지만 다만 서로 다른 서버의 DB가 데이터를 주고 받는 과정에서 보안이 중요하다.
- 기본적으로 같은 네트워크를 사용해야 하므로 같은 VPC를 사용해야 한다.
- 또한 데이터 암호화를 위해 TLS 통신도 중요한데, Let's Encrypt 방식은 HTTP 프로토콜에 적합한 방식이므로 DB간 통신에는 적용하지 못하는 단점이 있다.
- 테이블 구조가 변경되거나, 데이터에 수정이 발생하면 트래픽은 쓰기 DB로 이어진다.
- 슬레이브 DB가 변경을 감지하여 자신의 상태를 업데이트한다.
- 슬레이브 DB가 복제하는 방식은 3가지 옵션이 존재
- STATEMENT: SQL 자체를 로그에 기록한다. SQL 자체를 복제하므로 일관된 결과를 얻을 수 있지만 NOW() 같은 명령어에서는 정합성을 보장할 수 없다.
- ROW: 변경된 ROW 자체를 로그에 기록한다. 가장 정확한 복제 정보를 제공하지만 많은 양의 데이터 변경이 있을경우 로그의 크기가 커질 수 있다.
- MIXED: 기본으로 STATEMENT이지만 필요한 경우 ROW 형식으로 수정한다. 이 방식은 STATEMENT의 효율성과 ROW의 정확성의 장점을 가진다.
마스터 DB 슬레이브 DB 구현 코드
- 마스터 DB 생성
- mysql-bin은 데이터 변경에 대한 기록을 포함하며 슬레이브 서버로 복제할 내용이 들어있음
- binlog-format은 복제 형식으로, ROW 형식는 데이터 변경을 더 명확하게 추적할 수 있게 해줌
- volumes를 통해 각 DB의 데이터를 로컬에 마운트하여 데이터 보존
version: "3.8"
services:
db_master:
image: mysql
command: --server-id=1 --log-bin=mysql-bin --binlog-format=ROW
environment:
MYSQL_ROOT_PASSWORD: masterdb
MYSQL_USER: dbuser
MYSQL_PASSWORD: dbpassword
MYSQL_DATABASE: sharedDB
volumes:
- ./masterdb:/var/lib/mysql
ports:
- "3306:3306"
volumes:
masterdb:
- 사용자에게 복제 슬레이브 권한을 줌
- 복제 슬레이브 권한은 마스터 서버의 데이터를 읽고 복제하는 데 필요하다.
CREATE USER 'replicator'@'%' IDENTIFIED BY 'replicatorpassword';
ALTER USER 'replicator'@'%' IDENTIFIED WITH mysql_native_password BY 'replicatorpassword';
GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'%';
FLUSH PRIVILEGES;
- 슬레이브 DB가 복제할 로그 파일 및 위치 정보를 조회할 수 있음
- 이 정보를 통해 연결
SHOW MASTER STATUS;
- --server-id를 통해 마스터 DB와 각 슬레이어 DB 고유 ID 구별
- Id를 통해 마스터 DB가 각 슬레이브 DB의 변경 감지 상황을 체크할 수 있음
version: "3.8"
services:
db_slave:
image: mysql
command: --server-id=2
environment:
MYSQL_ROOT_PASSWORD: slavedb
MYSQL_USER: dbuser
MYSQL_PASSWORD: dbpassword
MYSQL_DATABASE: sharedDB
MASTER_HOST: db_master
volumes:
- ./slavedb:/var/lib/mysql
ports:
- "3306:3306"
volumes:
slavedb:
마스터 DB에서 생성한 복제 권한을 가진 유저를 통해 마스터 DB 서버로 연결
CHANGE MASTER TO
MASTER_HOST='db_master',
MASTER_USER='replicator',
MASTER_PASSWORD='replicatorpassword',
MASTER_LOG_FILE='mysql-bin.000003',
MASTER_LOG_POS=1156,
MASTER_PORT=3306;
START SLAVE;
슬레이브 DB에서 마스터 DB와의 연결을 초기화 하거나 연결 상태 체크하는 명령어
SHOW SLAVE STATUS\G;
STOP SLAVE;
RESET SLAVE;
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
type: 'mysql',
replication: {
master: {
host: configService.get<string>('DB_HOST'),
port: +configService.get<number>('DB_PORT'),
username: configService.get<string>('DB_USERNAME'),
password: configService.get<string>('DB_PASSWORD'),
database: configService.get<string>('DB_DATABASE'),
},
slaves: [
{
host: configService.get<string>('SLAVE_DB_HOST'),
port: +configService.get<number>('SLAVE_DB_PORT'),
username: configService.get<string>('SLAVE_DB_USERNAME'),
password: configService.get<string>('SLAVE_DB_PASSWORD'),
database: configService.get<string>('SLAVE_DB_DATABASE'),
},
],
},
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: false,
logging: ['query', 'error'],
}),
}),