먼저, MySQL 온라인 스키마 마이그레이션 이라고 하면 MySQL 5.6부터 온라인 DDL이 가능하다.
병렬로 DML이 실행되어도 락이 걸리지 않고 스키마 변경이 가능하다. 특히, MySQL 8.0부터 Instance Add Column 라고 테이블을 리빌드 하지 않고 컬럼을 추가할 수 있는 아주 좋은 기능이 추가되었다.
하지만, int -> bigint로 변경하는 ALTER 구문 등 몇 가지의 조작은 병렬 DML이 허용되지 않는다. 즉 테이블 전체가 락이 걸리게 되고 복제 지연이 발생할 가능성이 있다. 조작에 따라 Alter 중 가능한 동작이 다르므로 주의가 필요하다.
상세한 내용은 공식 사이트 참고.
https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html
gh-ost를 사용하면, 몇 가지 제약은 있지만 복제 지연이나 부하를 컨트롤 할 수 있고 병렬 DML도 허용하기 때문에 서비스 가동 중에 스키마 마이그레이션이 가능하다.
아키텍처
gh-ost는 기본적으로 아래와 같이 동작한다.
- 변경 하려고 하는 테이블을 복사하여 빈 테이블(고스트 테이블) 작성
- 고스트 테이블에 대하여 지정한 ALTER 구문 실행
- 기존 테이블의 데이터를 고스트 테이블에 복사
- 마이그레이션 실행 중, 기존 테이블의 신규 DML은 바이너리 로그로 부터 추출해서 고스트 테이블에 적용
# 3, 4는 병렬로 가동 - 3, 4가 끝나면 기존 테이블과 고스트 테이블 교체
온라인 스키마 마이그레이션 툴로 유명한 Percona가 공개하고 있는 pt-online-schema-change(pt-osc) 가 있다. pt-osc와 gh-ost 기본 아키텍처는 비슷하지만, 다른점은 신규 DML을 적용하는 방법이다.
pt-osc는 트리거를 이용하지만, gh-ost는 바이너리 로그를 이용한다.
따라서, 트리거에 의해 오버헤드가 없는 만큼 gh-ost가 유연하게 부하를 억제할 수 있다.
gh-ost는 최대한 마스터의 부하를 억제하지 위해서 슬레이브를 활용하도록 설계되어 있다. 즉, 기본적으로 바이너리 로그를 습득하는 곳은 슬레이브이다.
1, 2, 3, 5는 마스터에서 실행되고 4번의 바이너리 로그 습득은 슬레이브로 부터, 추출한 DML 적용은 마스터로 실행하게 된다.
물론, 설정에 따라 바이너리 로그 습득을 마스터에서 실행할 수도 있다.
필수조건
gh-ost를 실행하기 위해서는 아래의 권한이 필요하다.
제한 항목pt-osc는 트리거를 이용하지만, gh-ost는 바이너리 로그를 이용한다.
따라서, 트리거에 의해 오버헤드가 없는 만큼 gh-ost가 유연하게 부하를 억제할 수 있다.
gh-ost는 최대한 마스터의 부하를 억제하지 위해서 슬레이브를 활용하도록 설계되어 있다. 즉, 기본적으로 바이너리 로그를 습득하는 곳은 슬레이브이다.
1, 2, 3, 5는 마스터에서 실행되고 4번의 바이너리 로그 습득은 슬레이브로 부터, 추출한 DML 적용은 마스터로 실행하게 된다.
물론, 설정에 따라 바이너리 로그 습득을 마스터에서 실행할 수도 있다.
필수조건
gh-ost를 실행하기 위해서는 아래의 권한이 필요하다.
- ALTER, CREATE, DELETE, DROP, INDEX, INSERT, LOCK, TABLES, SELECT, TRIGGER, UPDATE 권한을 대상 데이터베이스로 작성
- SUPER, REPLICATION CLIENT, REPLICATION SLAVE on *.*이 필요한 경우 있음
2번의 권한은 --switch-to-rbr 옵션을 사용할 때 필요하다. 이 옵션은 슬레이브의 binlog_format이 ROW가 아닌 경우, binlog_format을 ROW로 변경하고 STOP SLAVE와 START SLAVE를 실행한다.
binlog_format이 ROW인 경우에는 --assume-rbr 옵션을 사옹하여 STOP SLAVE, START SLAVE를 실행안하는 게 좋다.
복제 설정
gh-ost가 슬레이브에서 바이너리 로그를 습득하기 위해 필요한 MySQL 설정은 아래와 같다.
binlog_format이 ROW인 경우에는 --assume-rbr 옵션을 사옹하여 STOP SLAVE, START SLAVE를 실행안하는 게 좋다.
복제 설정
gh-ost가 슬레이브에서 바이너리 로그를 습득하기 위해 필요한 MySQL 설정은 아래와 같다.
- log_bin
- log_slave_updated=ON
- binlog_format=ROW
- binlog_row_image=FULL
마스터가 binlog_format=STATEMENT or MIXED로 설정되어 있어도 문제 없다. 슬레이브만 위와 같이 설정하면 gh-ost는 실행 가능하다.
마스터와 슬레이브 간 설정이 다른 게 불안하다면, gh-ost실행 후 설정을 되돌리면 된다.
마스터와 슬레이브 간 설정이 다른 게 불안하다면, gh-ost실행 후 설정을 되돌리면 된다.
아래의 조건에 해당하는 게 하나라도 있으면 에러가 발생한다.
- 대상 테이블에 Primary key 또는 NOT NULL 제약이 붙은 유니크 키가 없는 경우
- 대상 테이블에 외래 키 제약이 있는 경우
- 대상 테이블에 트리거가 있는 경우
- 대상 테이블의 Primary key에 JSON 컬럼이 포함되어 있는 경우
테스트
이제부터 gh-ost를 실제로 사용해보자.
설치
실행
sbtest1 테이블에 col1 컬럼을 추가하는 gh-ost를 실행해보자.
--host: 슬레이브 호스트명. 마스터의 정보는 gh-ost가 내부적으로 검색한다.
--alter: 실행하려고 하는 Alter 구문
이제부터 gh-ost를 실제로 사용해보자.
설치
# wget https://github.com/github/gh-ost/releases/download/v1.1.0/gh-ost-1.1.0-1.x86_64.rpm # rpm -i gh-ost-1.1.0-1.x86_64.rpm
실행
CREATE TABLE `sbtest1` ( `id` int NOT NULL AUTO_INCREMENT, `k` int NOT NULL DEFAULT '0', `c` char(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '', `pad` char(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `k_1` (`k`) ) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
sbtest1 테이블에 col1 컬럼을 추가하는 gh-ost를 실행해보자.
gh-ost \ --host=127.0.0.1 \ --port=3306 \ --user="ghost_user" \ --password="xxxxxx" \ --database="test_db" \ --alter="ALTER TABLE sbtest1 ADD col1 int;" \ --execute
--host: 슬레이브 호스트명. 마스터의 정보는 gh-ost가 내부적으로 검색한다.
--alter: 실행하려고 하는 Alter 구문
--execute: 실제로 실행. 지정하지 않으면 테스트만 실행한다.
결과
sbtest1 테이블에 col1 컬럼이 추가되어 있는 것을 확인할 수 있다.
기존 테이블은 아래와 같이 확인할 수 있다.
기본적으로 _TABLENAME_del 과 같은 네이밍으로 변경된다. DROP은 cost 높은 처리이므로, 기존 테이블을 바로 DROP 하지 않는다. --ok-to-drop-table 옵션을 사용하면 실행 종료 타이밍에 기존 테이블을 DROP시킬 수 있다.
gh-ost 실행은 아래와 같은 순서로 하는 게 좋을 거 같다.
결과
sbtest1 테이블에 col1 컬럼이 추가되어 있는 것을 확인할 수 있다.
CREATE TABLE `sbtest1` ( `id` int NOT NULL AUTO_INCREMENT, `k` int NOT NULL DEFAULT '0', `c` char(120) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '', `pad` char(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '', `col1` int DEFAULT NULL, PRIMARY KEY (`id`), KEY `k_1` (`k`) ) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
기존 테이블은 아래와 같이 확인할 수 있다.
mysql> show tables like '_sbtest1_del'; +-------------------------------------+ | Tables_in_sysbenchdb (_sbtest1_del) | +-------------------------------------+ | _sbtest1_del | +-------------------------------------+
기본적으로 _TABLENAME_del 과 같은 네이밍으로 변경된다. DROP은 cost 높은 처리이므로, 기존 테이블을 바로 DROP 하지 않는다. --ok-to-drop-table 옵션을 사용하면 실행 종료 타이밍에 기존 테이블을 DROP시킬 수 있다.
gh-ost 실행은 아래와 같은 순서로 하는 게 좋을 거 같다.
- --execute 옵션 없이 실행 후, 에러가 발생하는 지 확인
- --execute 옵션 적용 후, 실제로 실행
댓글 없음:
댓글 쓰기