MySQL 레플리카 서버가 비정상 종료되는 경우 서버를 다시 구동시켰을 때, 소스 서버와의 복제 동기화가 실패할 수 있고, 이로 인해 최악의 경우 서비스가 중단될 수 있다.
- 레플리카 서버 재구축하는 동안 또 다른 레플리카 서버가 존재하지 않는다면 소스 서버는 레플리카 서버가 복구될 때까지 예비 서버 없이 운영되어야 하며 복구되는 동안 소스 서버에도 장애가 발생하는 경우 서비스가 중단될 수 있다.
이러한 상황을 대비하기 위해 비정상 종료된 후 재구동됐을 때도 복제가 원활하게 재개될 수 있게 여러 설정을 제공하며, 이를 통해 사용자는 서버 장애 이후에도 문제없이 복제가 진행되는 크래시 세이프 복제를 실현할 수 있다.
- 크래시 세이프 복제는 옵션을 활성화해서 적용하는 기능이 아니라 여러 가지 복제 관련 옵션들을 복제 사용 형태에 따라 적절히 설정했을 때 얻게 되는 효과라 할 수 있다.
서버 장애와 복제 실패
MySQL 복제는 레플리케이션 I/O 스레드와 SQL 스레드가 협업하여 소스 서버와의 동기화를 수행한다.
- I/O 스레드
- 소스 서버로부터 바이너리 로그 이벤트를 네트워크를 통해 가져온 후 레플리카 서버의 로컬 디스크에 파일로 저장하는 역할을 담당.
- SQL 스레드
- I/O 스레드가 가져온 바이너리 로그 이벤트를 실제 MySQL 서버에서 재실행하는 역할 담당.
동기화 과정에서 I/O 스레드와 SQL 스레드는 각각 자신이 어느 시점까지의 바이너리 로그를 가져왔는지, 어느 트랜잭션까지 재실행했는지에 대한 포지션 정보를 남기며, 서버가 종료되었다가 재실행되면 포지션 정보들을 참조해서 복제를 어느 시점 부터 다시 시작해야 할지를 판단하게 된다.
- 실행 포지션 정보는 시스템 변수들에 설정된 값에 따라 파일, 테이블 형태로 관리된다.
파일 형태로 관리될 경우 각 스레드가 동작할 때 실제 자신이 처리 중인 내용과 포시션 정보를 원자적으로 동기화된 상태로 관리할 수 없어 처리한 내역과 포지션 정보 간에 불일치가 발생할 수 있었다.
- I/O 스레드가 릴레이 로그에 이벤트를 기록한 후 포지션 정보 파일에 업데이트를 하지 않은 상태에서 비정상 종료시 재구동할 때 릴레이 로그에 동일한 이벤트가 기록될 수 있다.
- SQL 스레드가 릴레이 로그에 기록된 트랜잭션을 커밋한 후 포지션 정보 파일에 업데이트를 하지 않은 상태에서 비정상 종료되면 재구동시 동일한 트랜잭션이 재실행될 수 있다.
이러한 불일치로 인해 동리한 INSERT
쿼리가 두번 실행되었을 때 발생할 수 있는 Duplicate key 에러가 발생하게된다.
- 상황에 따라 다양한 에러가 발생할 수 있으며 최악의 경우 에러 없이 데이터가 잘못될 수도 있다.
테이블로 관리하는 경우 InnoDB 엔진을 사용하므로 SQL 스레드가 트랜잭션 적용과 포지션 정보 업데이트를 한 트랜잭션으로 묶어 원자적으로 처리할 수 있어 SQL 스레드가 동일한 쿼리를 재실행하는 문제는 방지할 수 있게 되었다.
I/O 스레드의 포지션 불일치 문제는 relay_log_recovery 옵션이 도입되며 해결되었다.
- 재구동 시 I/O 스레드의 포지션을 마지막으로 실행했던 포지션으로 초기화한다.
- 새로운 릴레이 로그 파일을 생성해서 SQL 스레드가 읽어야 할 릴레이 로그 포지션 위치를 초기화한다.
|
|
설정한다고 하더라도 운영체제의 비정상 종료시 데이터 파일 자체가 손상될 수 있으므로 무용지물일 수 있다.
복제 사용 형태별 크래시 세이프 복제 설정
MySQL 복제는 사용자가 설정한 내용에 따라 복제 타입 및 동기화 방식이 다르므로 그에 따라 크래시 세이프 복제 설정도 조금씩 달라질 수 있다.
바이너리 로그 파일 위치 기반 복제 + 싱글 스레드 동기화
최소 옵션 설정 셋과 동일하다.
바이너리 로그 파일 위치 기반 복제 + 멀티 스레드 동기화
레플리카 서버에서 복제된 트랜잭션들의 커밋 순서가 소스 서버에서와 동일하도록 설정됐는지 여부에 따라 크래시 세이프 복제를 위한 옵션 셋이 달라진다.
소스 서버와 트랜잭션 커밋 순서 일치 여부 | 크래시 세이프 복제 설정 |
---|---|
일치 | 최소 설정 |
불일치 | 최소 설정 + sync_relay_log=1 |
sync_relay_log
- 릴레이 로그를 디스크와 얼마나 자주 동기화할 것인지 제어(기본값 10000)
- 0: MySQL에서는 동기화 X, 운영체제 설정에 따라 도익화 수행
- 1이상: 릴레이 로그에 지정된 수만틈 이벤트가 기록됐을 때 동기화 수행
- 1이 아닌 값을 사용하게 되면 비정상 종료시 동기화되지 못한 릴레이 로그의 내용이 유실될 수 있어 갭이 발생 가능하고 이로인해 복제가 실패할 수 있다.
옵션을 1로 설정하면 이벤트가 릴레이 로그에 기록될 때마다 디스크에도 동기화가 처리되므로 이벤트 손실을 최소화 할 수 있으나 디스크에 부하를 주게 되며, 복제 시 성능이 저하될 수 있다.
따라서 멀티 스레드 복제를 사용할 때는 LOGICAL_LOCK 방식을 사용하고 slave_preserve_commit_order
옵션을 1로 설정하여 갭이 발생 하지 않게 하여 sync_relay_log=1
설정 없이도 크래시 세이프한 복제가 될 수 있게 하는 것을 권장한다.
GTID 기반 복제 + 싱글 스레드 동기화
mysql.gtid_executed
테이블 데이터가 복제된 트랜잭션이 적용될 때마다 매번 함께 갱신되는지 여부에 따라 옵션 셋이 달라진다.
gtid_executed 테이블 데이터가 매 트랜잭션 적용 시 갱신되는 경우
|
|
gtid_excuted 테이블 데이터가 매 트랜잭션 적용 시 갱신되지 않는 경우
|
|
GTID 기반 복제에서는 [MASTER | SOURCE]_AUTO_POSITION=1
옵션을 사용하는데, 이 경우 SQL 스레드가 마지막으로 적용한 트랜잭션의 GTID를 얻기 위해 복구 시 mysql.slave_relay_log_info
테이블이 아닌 mysql.gtid_excuted
테이블 데이터를 참조한다.
- 이에 따라
relay_log_info_repositiory=TABLE
옵션이 제외된다.
gtid_executed 테이블이 매 트랜잭션이 적용될 때마다 항상 함께 갱신되어야 정상적으로 복구사 수행된다.
- gtid_executed 테이블 테이터는 MySQL 8.0.17 버전부터는 기본적으로 매 트랜잭션 적용 시 함께 갱신된다.
- MySQL 8.0.17 미만 버전을 테이블 설정에 따라 달라진다.
gtid_executed 테이블의 데이터가 매 트랜잭션이 적용될 때마다 갠싱되지 않는다면 MySQL 서버의 바아너리 로그를 통해 누락된 트랜잭션들의 GTID들을 복구해야 하므로 sync_binlog=1
, innodb_flush_log_at_trx_commit=1
옵션이 반드시 설정돼 있어야 한다.
GTID 기반 복제 + 멀티 스레드 동기화
싱글 스레드로 동기화되는 경우와 동일하다.
유의할 점은 멀티 스레드 복제인 경우 앞에서 언급한 것처럼 비정상적으로 종료된 후 relay_log_recovery=ON
설정으로 재구동 시 트랜잭션 갭을 메우는 작업이 수행된다.
이는 SOURCE_AUTO_POSITION
옵션을 사용하는 GTID 기반 복제에서는 불필요하지만, 옵션 사용 여부와 관계없시 실행되는데 이러한 처리로 인해 오히려 크래시가 발생해 릴레이 로그에 이벤트가 손실되면 앞서 언급한 것과 동일하게 복구 작업 시 갭 메우기 작업이 실패하면서 복제 연결이 실패하는 현상이 발생할 수 있다.
MySQL 8.0.18/5.7.28 버전부터는 GTID 기반이면서 SOURCE_AUTO_POSITION
옵션을 사용하는 경우에 대해서 이러한 작업이 자동으로 생략되도록 갯너되었다.