마스터 DB 슬레이브 DB

JSM·2023년 11월 21일
0

프로젝트

목록 보기
4/10
post-thumbnail

왜 마스터 DB와 슬레이브 DB를 사용할까?

  • AWS 자격증을 따면서 왜 RDS, Aroura, DynamoDB를 사용하는지 궁금했다.
  • 가장 큰 이유는 Multi AZ와 읽기 전용 복제본이라고 말할 것 같다.
  • Multi AZ는 여러 가용영역에 복제본을 만드는 것이다. 따라서 한 가용영역에 문제가 생겨도 다른 지역의 DB로 복구하는 것이다.
  • 읽기 전용 복제본은 읽기 기능만 가능한 DB를 만들어 읽기와 쓰기 작업을 하는 DB를 분리하는 것이다.

읽기 전용 복제본을 사용하는 이유가 뭘까?

  • DB에서 거의 대부분의 작업이 읽기 작업이다. 읽기랑 쓰기가 같은 DB에서 일어난다고 해보자.
  • 만약 DB에 부하가 심해져서 다운되면?? 읽기 전용 복제본을 여러개 만들어서 부하를 분산하는 것이다.

마스터 DB와 슬레이브 DB는 다른 서버에서 관리하자.

  • 처음에 마스터 DB와 슬레이브 DB를 하나의 서버에서 관리했다.
  • 이 방식은 트래픽을 분리해줄 지 몰라도 서버 자체에 트래픽이 몰리는 것은 변함이 없다.
  • 따라서 더 효율적으로 트래픽을 관리하고 싶다면 서버를 분리하는 것이 맞다.
  • 하지만 다만 서로 다른 서버의 DB가 데이터를 주고 받는 과정에서 보안이 중요하다.
  • 기본적으로 같은 네트워크를 사용해야 하므로 같은 VPC를 사용해야 한다.
  • 또한 데이터 암호화를 위해 TLS 통신도 중요한데, Let's Encrypt 방식은 HTTP 프로토콜에 적합한 방식이므로 DB간 통신에는 적용하지 못하는 단점이 있다.

마스터 DB와 슬레이브 DB 동작 방식

  • 테이블 구조가 변경되거나, 데이터에 수정이 발생하면 트래픽은 쓰기 DB로 이어진다.
  • 슬레이브 DB가 변경을 감지하여 자신의 상태를 업데이트한다.

슬레이브 DB의 복제 방식

  • 슬레이브 DB가 복제하는 방식은 3가지 옵션이 존재
  • STATEMENT: SQL 자체를 로그에 기록한다. SQL 자체를 복제하므로 일관된 결과를 얻을 수 있지만 NOW() 같은 명령어에서는 정합성을 보장할 수 없다.
  • ROW: 변경된 ROW 자체를 로그에 기록한다. 가장 정확한 복제 정보를 제공하지만 많은 양의 데이터 변경이 있을경우 로그의 크기가 커질 수 있다.
  • MIXED: 기본으로 STATEMENT이지만 필요한 경우 ROW 형식으로 수정한다. 이 방식은 STATEMENT의 효율성과 ROW의 정확성의 장점을 가진다.

마스터 DB 슬레이브 DB 구현

마스터 DB 슬레이브 DB 구현 코드

1. 마스터 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:

마스터 DB 데이터 복제 권한을 가진 계정 생성

  • 사용자에게 복제 슬레이브 권한을 줌
  • 복제 슬레이브 권한은 마스터 서버의 데이터를 읽고 복제하는 데 필요하다.
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;

2. 슬레이브 DB 구성

  • --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;

3. Nest.js 서버에서 트래픽 분산

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'],
  }),
}),
profile
내 기술적 고민들을 모은 곳...

0개의 댓글