Last Updated on 1월 9, 2024 by Jade(정현호)
안녕하세요
이번 포스팅에서는 그룹 복제 모드 중에서 Multi Primary 와 Single Primary 각각에 대해서 조금 더 자세하게 내용을 확인해보도록 하겠으며, 그룹 복제에서의 트랜잭션 일관성 보장 관련된 내용도 알아보도록 하겠습니다.
해당 포스팅은 아래 포스팅에서 이어지는 글입니다.
Contents
그룹 복제 모드
InnoDB Cluster 는 Group Replication 과 MySQL Router, MySQL Shell 기술을 사용한 고가용성 및 확장 기능을 제공하는 솔루션입니다. 그 중에서 복제와 HA 주 기능은 그룹 복제(Group Replication) 을 통해서 이루어지게 됩니다.
이러한 InnoDB Cluster 주요 구성 요소인 Group Replication 은 경우 Primary 를 몇 개로 사용할 지에 따른 Single Primary Mode 와 Multi Primary Mode 2가지 방식을 지원하며 구성 시 기본값은 Single Primary 모드입니다.
그룹 모드는 group_replication_single_primary_mode 시스템 변수로 지정되는 그룹 전체 구성 설정이며 모든 구성원에서 동일해야 합니다. 시스템변수 ON 은 기본 모드인 단일 기본 모드(Single Primary Mode) 이며 OFF는 다중 기본 모드(Multi Primary Mode)를 의미합니다. 예를 들어 한 구성원은 다중 기본 모드로 구성되고 다른 구성원은 단일 기본 모드로 구성되는 것과 같이 그룹 구성원을 서로 다른 모드로 배포하는 것은 불가능합니다.
이전의 (8.0.13 미만) 버전에서는 그룹 복제가 실행되는 동안에는 group_replication_single_primary_mode 시스템 변수를 변경할 수 없었으며, MySQL 8.0.13 버전 부터는 그룹 복제가 실행되는 동안에도 2개의 function 을 사용하여 다른 모드로 그룹을 이동 시킬수 있게 되었습니다.
• group_replication_switch_to_single_primary_mode()
• group_replication_switch_to_multi_primary_mode()
이러한 기능은 그룹 모드 변경 프로세스가 관리하고 데이터의 안전성과 일관성을 보장합니다. 이전 릴리스(8.0.13 미만 버전)에서 그룹의 모드를 변경하려면 그룹 복제를 중지하고 모든 구성원의 group_replication_single_primary_mode 시스템 변수 값을 변경 후에 복제를 다시 시작해야 모드가 변경할 수 있었습니다.
Group Replication 에는 이렇게 Single Primary Mode 와 Multi Primary Mode 를 지원하며 각각에 대해서 확인해보도록 하겠습니다.
Single Primary Mode
Group Replication 에서 단일 기본 모드(group_replication_single_primary_mode=ON) 의 경우 그룹내 1개 서버에서만 읽기-쓰기가 가능한 상태를 의미합니다. 그 외 그룹내 다른 구성원(인스턴스)은 읽기 전용 모드로 설정 됩니다.(super_read_only=on)
그룹 복제에서 단일 기본 모드는 그룹내에서 단일 서버만 쓰도록(Write) 강제하므로 다중 기본 모드와 비교하여 일관성 검사가 덜 엄격할 수 있고 DDL 문을 특별히 주의하여 처리할 필요가 없습니다.
group_replication_enforce_update_everywhere_checks 옵션은 그룹에 대한 엄격한 일관성 검사를 활성화하거나 비활성화합니다. 기본값은 검사를 비활성화하는 것입니다.
단일 기본 모드로 배포하거나 그룹을 멀티 기본 모드(Multi Primary Mode) 에서 단일 기본 모드(Single Primary Mode)로 변경하는 경우 이 시스템 변수를 OFF 로 설정해야 합니다.
추가 정보로 다중 기본 모드에서 이 옵션이 활성화되면 트랜잭션이 다중 기본 모드와 호환되는지 확인하기 위해 다음과 같이 확인됩니다.
-
트랜잭션이 SERIALIZABLE 격리 수준에서 실행되면 그룹과 자신을 동기화할 때 커밋이 실패합니다.
-
계단식 제약 조건이 있는 외래 키가 있는 테이블에 대해 트랜잭션이 실행되는 경우 해당 트랜잭션은 그룹과 동기화할 때 커밋에 실패합니다.
그룹에 새롭게 추가할 구성원(인스턴스)의 시스템 변수에 대해 그룹과 다른 값이 설정되어 있으면 값이 일치하도록 변경될 때까지 구성원이 그룹에 참여할 수 없습니다.
MySQL 8.0.16부터 group_replication_switch_to_single_primary_mode() 및 group_replication_switch_to_multi_primary_mode() 함수를 사용하여 그룹의 모드를 변경할 경우 실행 과정에서 이 시스템 변수의 값을 변경할 수 있으며 MySQL Shell 에서는 switchToSinglePrimaryMode 또는 switchToMultiPrimaryMode 명령어를 통해서 모드 변경 시 group_replication_enforce_update_everywhere_checks 시스템 변수도 같이 변경되게 됩니다.
Single Primary Mode 를 사용하는 환경에서 다음과 같은 경우에는 다른 예비 서버로 Primary 가 변경될 수 있습니다.
- 자발적이거나 예상치 못하게 Primary 서버가 그룹에서 떠나게 되었을 경우 새로운 Primary 를 선출하게 됩니다.
- group_replication_set_as_primary() 기능을 사용하여 특정 구성원을 Primary 인스턴스로 지정할 수 있습니다.
- group_replication_switch_to_single_primary_mode() 함수를 사용하여 다중 기본 모드에서 실행 중인 그룹을 단일 기본 모드로 실행하도록 변경하면 새 기본이 자동으로 선택되거나 함수로 지정하여 새 기본을 지정할 수 있습니다.
위의 기능은 모든 그룹 구성원이 MySQL 8.0.13 이상을 실행하는 경우에만 사용할 수 있습니다. 새로운 Primary 서버가 자동으로 선택되거나 수동으로 지정되어 변경될 경우, 해당 서버는 자동으로 읽기-쓰기로 설정되고 다른 그룹 구성원은 보조 서버로 남아 있으므로 읽기 전용(read only) 로 설정되게 됩니다.
[group-replication-single-primary-mode - New Primary Election]
새로운 Primary 가 선택되거나 임명되면 이전 Primary 서버에는 적용되었지만 새롭게 선출된 Primary 서버에는 아직 적용되지 않은 변경 사항의 백로그가 있을 수 있습니다. 이 상황에서 새로운 Primary 에서의 실행된 트랜잭션이 이전 Primary 서버 트랜잭션을 따라잡을 때까지(그 과정에서) 읽기-쓰기 트랜잭션과 충돌을 일으켜 롤백 될 수 있으며 읽기 전용 트랜잭션으로 인해 stale reads가 발생할 수 있습니다.
복제의 빠른 구성원과 느린 구성원 간의 차이를 최소화하는 그룹 복제의 흐름 제어(flow-control) 메커니즘은 활성화되고 적절하게 조정된 경우 이러한 상황이 발생할 가능성을 줄입니다.
MySQL 8.0.14부터는 group_replication_consistency 시스템 변수를 사용하여 그룹의 트랜잭션 일관성 수준을 구성하여 이 문제를 방지할 수도 있습니다. BEFORE_ON_PRIMARY_FAILOVER(또는 더 높은 일관성 수준) 설정은 아직 적용되지 못한 백로그가 적용될 때까지 새로 선택된 Primary 서버에서의 새로운 트랜잭션을 holds 합니다.
만약 흐름 제어 및 트랜잭션 일관성 보장이 그룹에 사용되지 않는 경우 클라이언트 응용 프로그램을 다시 라우팅하기 전에 새 기본이 복제 관련 릴레이 로그를 적용할 때까지 기다리는 것이 좋습니다.
보다 자세한 내용은 아래에서 트랜잭션 일관성 보장 관련 내용에서 확인 해보시기 바랍니다.
새로운 Primary 선출 방식
사용자에 의한 Primary 변경이 아닌 장애 등에 의한 자동적인 Primary 인스터스의 선택 프로세스에는 각 구성원이 그룹의 새로운 관점을 보고 잠재적인 새로운 Primary 인스턴스를 주문하고 가장 적합한 구성원을 선택하는 작업이 포함됩니다.
그룹의 각 구성원은 MySQL Server 릴리스의 기본 선택 알고리즘에 따라 로컬에서 자체 결정을 내립니다. 모든 구성원이 동일한 결정에 도달해야 하기 때문에 구성원은 다른 그룹 구성원이 더 낮은 MySQL Server 버전을 실행하는 경우 기본 선택 알고리즘을 적용하여 그룹에서 가장 낮은 MySQL Server 버전을 가진 구성원과 동일한 동작을 하도록 합니다.
Primary 인스턴스의 선출시에 고려하는 요소의 순서는 다음과 같습니다.
- 고려되는 첫번째 요소로는 가장 낮은 MySQL Server 버전을 실행하는 구성원(멤버) 확인하는 것으로, 이 과정을 진행하기 위해서 그룹 내에서 버전으로 정렬을 시도하게 됩니다.
버전에 따라서 정렬의 방식이 약간은 다르며 모든 그룹의 멤버가 MySQL 8.0.17 이상을 사용하는 경우 멤버는 패치 버전에 따라서 먼저 정렬됩니다.
MySQL Server 5.7 버전 또는 MySQL 8.0.16 이하 버전을 사용하는 경우 주요(메이저) 버전으로 먼저 정렬되고 패치 버전은 무시되게 됩니다.
- 두 번째 요소는 그룹내에서 둘 이상의 멤버가 가장 낮은 MySQL Server 버전을 실행하는 경우 멤버의 group_replication_member_weight 시스템 변수 값을 통해서 가중치 값을 비교하게 됩니다. 그룹의 구성원이 이 시스템 변수를 사용할 수 없는 MySQL Server 5.7 버전을 사용할 경우 이 요소는 무시됩니다.
해당 시스템 변수는 MySQL 5.7.20 버전 부터 추가되었습니다. 기본 값은 50으로 최소 0 부터 최대 100까지 지정할 수 있습니다.
예를 들어 아래와 같이 멤버 별로 시스템 변수가 설정이 되어 있다고 하였을 때
member-1: group_replication_member_weight=30, server_uuid=aaaa
member-2: group_replication_member_weight=40, server_uuid=bbbb
member-3: group_replication_member_weight=40, server_uuid=cccc
member-4: group_replication_member_weight=40, server_uuid=dddd
member-2, member-3, member-4, and member-1 순으로 정렬되고 최종적으로 member-2가 새로운 Primary 서버로 선택되어 Failover 되게 됩니다.
- 고려되는 세 번째 요소로 둘 이상의 구성원이 가장 낮은 MySQL Server 버전을 실행 중이고 해당 구성원(멤버) 중 둘 이상의 구성원이 가장 높은 구성원 가중치를 갖는 경우(또는 구성원 가중치가 무시되는 경우), 각 구성원의 생성된 서버 UUID 의 사전식 순서를 확인하게 됩니다. server_uuid 시스템 변수에 지정된 대로 가장 낮은 서버 UUID 를 가진 구성원이 기본으로 선택됩니다. 이 요소는 중요한 요소에 의해 결정될 수 없는 경우 모든 그룹 구성원이 동일한 결정에 도달할 수 있도록 보장되고 예측 가능한 타이 브레이커(tie-breaker) 역할을 합니다.
tie-breaker
타이브레이크 또는 타이브레이커는 스포츠나 게임에서 여럿이 동률일 때 그중에서 승자나 앞선 순위를 가진 사람을 결정하는 제도입니다.(Ref URL)
member_weight 변경
member_weight 를 변경하는 방법은 크게 3가지입니다.
cluster.setInstanceOption
## 클러스터 옵션 조회 var cluster = dba.getCluster() cluster.options() ## 개별 클러스터 인스턴스의 설정 변경 var cluster = dba.getCluster() cluster.setInstanceOption('ic-server3:3306', 'memberWeight',2)
SET PERSIST
SET PERSIST group_replication_member_weight = 5;
SET
set global group_replication_member_weight=50;
cluster.setInstanceOption는 실제로 SET PERSIST를 사용하여 변경을 파라미터를 변경을 합니다.
datadir 아래에 있는 mysqld-auto.cnf 파일에서 다음과 같이 내용을 확인할 수 있습니다.
[mysql]$ cat mysqld-auto.cnf \ | jq '.mysql_static_variables.group_replication_member_weight' { "Value": "2", <-- 변경한 값 "Metadata": { "Host": "ic-server3", <-- 파라미터 변경을 실행한 Host "User": "clusteradm", <-- 파라미터 변경을 실행한 User "Timestamp": 1704803200480337 } }
set global 명령어로 변경한다면 인스턴스 재시작에 대비해서 my.cnf 파일에 별도로 파라미터를 기록해야 합니다.
SET PERSIST에 대한 자세한 내용은 아래 포스팅을 참조하시면 됩니다.
Primary 인스턴스 찾기
사용중인 Group Replication 에서 Single Primary 모드로 사용 중 일 때, Primary 로 동작하는 서버를 찾기 위해서는 performance_schema.replication_group_members 테이블을 이용하시면 됩니다.
mysql> select MEMBER_HOST,MEMBER_PORT,MEMBER_STATE,MEMBER_ROLE,MEMBER_VERSION from performance_schema.replication_group_members; +-------------+-------------+--------------+-------------+----------------+ | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | +-------------+-------------+--------------+-------------+----------------+ | ic-server1 | 3306 | ONLINE | PRIMARY | 8.0.23 | | ic-server2 | 3306 | ONLINE | SECONDARY | 8.0.23 | | ic-server3 | 3306 | ONLINE | SECONDARY | 8.0.23 | +-------------+-------------+--------------+-------------+----------------+
Group Replication 만 사용한다면 위와 같이 조회하면 되며, InnoDB Cluster 로 구성하여 MySQL Shell 을 사용한다면 아래와 같이 클러스터 상태 조회로도 내용을 확인할 수 있습니다.
JS > var cluster = dba.getCluster() JS > cluster.status(); { "clusterName": "JadeCluster", "defaultReplicaSet": { "name": "default", "primary": "ic-server1:3306", "ssl": "REQUIRED", "status": "OK", "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", "topology": { "ic-server1:3306": { "address": "ic-server1:3306", "mode": "R/W", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.23" }, "ic-server2:3306": { "address": "ic-server2:3306", "mode": "R/O", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.23" }, "ic-server3:3306": { "address": "ic-server3:3306", "mode": "R/O", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.23" } }, "topologyMode": "Single-Primary" }, "groupInformationSourceMember": "ic-server1:3306"
Multi Primary Mode
다중 기본 모드(group_replication_single_primary_mode=OFF) 에서는 구성원에게 특별한 역할이 없습니다. 그래서 클라이언트 또는 애플리케이션은 어느 멤버에게 라도 쓰기 트랜잭션을 요청할 수 있고 그룹은 해당 트랜잭션을 처리할 수 있습니다.
예를 들어 예기치 않은 서버 종료의 경우 그룹 복제는 클라이언트 측 장애 조치 자체를 처리하지 않으므로 MySQL Router 버전 8.0 과 같은 미들웨어 프레임워크나 프록시 또는 자체적인 커넥터 애플리케이션 사용하여 접속을 재 정렬해야 합니다.
[group-replication-multi-primary-mode - Client Failover]
위의 그림 "Client Failover" 는 멤버가 그룹을 떠날 경우 클라이언트가 대체 그룹 멤버에 다시 연결할 수 있는 방법을 보여줍니다.
그룹 복제는 최종 일관성 시스템입니다. 즉, 들어오는 트래픽이 느려지거나 중지되는 즉시 모든 그룹 구성원이 동일한 데이터 콘텐츠를 갖게 됩니다. 트래픽이 흐르는 동안 트랜잭션은 다른 구성원보다 먼저 일부 구성원에서 외부화(externalized)될 수 있습니다.
다중 기본 모드에서 느린 구성원은 인증 및 적용을 위해 과도한 트랜잭션 백로그를 생성할 수 있으므로 충돌 및 인증 실패의 위험이 커집니다. 이러한 문제를 제한하기 위해 그룹 복제의 흐름 제어 메커니즘을 활성화하고 조정하여 빠른 구성원과 느린 구성원 간의 차이를 최소화할 수 있습니다.
MySQL Server 버전 8.0.14 부터 그룹의 모든 트랜잭션에 대해 트랜잭션 일관성을 보장하려면 group_replication_consistency 시스템 변수를 사용하여 이를 수행할 수 있습니다. 이 시스템 변수의 기본값은 EVENTUAL 입니다.
일관성을 높이는 데 필요한 동기화의 성능 영향을 고려하여 그룹의 워크로드와 데이터 읽기 및 쓰기에 대한 우선 순위에 맞는 설정을 선택할 수 있습니다. 특히 동시성에 민감한 트랜잭션을 보호하기 위해 개별 세션에 대한 시스템 변수를 설정할 수도 있습니다.
(보다 자세한 내용은 아래 트랜잭션 일관성 보장에서 설명되어 있습니다.)
Transaction Checks
Group replication 이 Multi Primary Mode 인 경우 트랜잭션이 모드와 호환되는지 확인합니다. 그룹 복제가 Multi Primary Mode 로 사용될 때 다음과 같은 엄격한 일관성 검사가 수행됩니다.
-
트랜잭션이 SERIALIZABLE 격리 수준에서 실행되면 그룹과 자신을 동기화할 때 커밋이 실패합니다.
-
계단식 제약 조건이 있는 외래 키가 있는 테이블에 대해 트랜잭션이 실행되는 경우 해당 트랜잭션은 그룹과 동기화할 때 커밋에 실패합니다.
Check 는 group_replication_enforce_update_everywhere_checks 시스템 변수에 의해 제어됩니다. Multi Primary Mode 에서 시스템 변수는 일반적으로 ON 으로 설정되어야 하지만 시스템 변수를 OFF 로 설정하여 검사를 선택적으로 비활성화할 수 있습니다. Single Primary Mode 로 설정할 경우 해당 시스템 변수를 OFF 로 설정해야 합니다.
DDL 수행시
Multi Primary Mode 의 그룹 복제 토폴로지에서는 일반적으로 DDL(데이터 정의 언어)이라고도 하는 데이터 정의 문을 실행할 때 주의를 기울여야 합니다.
MySQL 8.0은 완전한 DDL 문이 단일 원자성 트랜잭션으로 커밋되거나 롤백되는 원자성 데이터 정의 언어(DDL) 문에 대한 지원을 도입했습니다. (System tablespace 가 InnoDB 로 변경됨)
그러나 DDL 문은 원자적(atomic)이거나 다른 방식으로 명령문을 실행하기 전에 COMMIT를 수행한 것처럼 현재 세션에서 활성화된 모든 트랜잭션을 암시적으로 종료합니다.
즉, DDL 문은 다른 트랜잭션 내에서, START TRANSACTION ... COMMIT와 같은 트랜잭션 제어 문 내에서, 또는 동일한 트랜잭션 내에서 다른 명령문과 결합될 수 없습니다.
Atomicity(원자성)
진행 중인 트랜잭션이 실패하게 될 경우 어느 단계는 적용되고 어느 단계의 데이터는 완료되지 않고 하는 경우가 발생해서는 안 되기 때문에 DBMS는 완료되지 않은 트랜잭션의 중간 상태를 데이터베이스에 반영해서는 안 됩니다.
즉, 트랜잭션의 모든 연산들이 정상적으로 수행 완료되거나 아니면 전혀 어떠한 연산도 수행되지 않은 상태를 보장해야 합니다. atomicity는 쉽게 'all or nothing' 특성으로 설명할 수 있습니다.
그룹 복제는 SQL 문장(statements)가 낙관적으로 실행되고 필요한 경우 나중에 롤백 되는 낙관적 복제 패러다임을 기반으로 합니다.
각 서버는 먼저 그룹 동의를 확보하지 않고 실행합니다. 따라서 다중 프라이머리 모드에서 DDL 문을 복제할 때 더 많은 주의가 필요합니다. 동일한 객체에 대해 스키마 변경(DDL 사용) 및 객체에 포함된 데이터 변경(DML 사용)을 수행하는 경우 스키마 작업이 아직 완료되지 않은 상태 그리고 모든 곳에서 복제가 진행되는 동안인 상태에서는 명령을 수행한 동일한 서버를 통해 변경 사항을 처리해야 합니다. (DDL 과 DML 을 서로 다른 서버에서 동시에 진행 시 유의 사항)
그렇게 하지 않으면 작업이 중단되거나 부분적으로만 완료된 경우 데이터 불일치가 발생할 수 있습니다. 그룹이 Single Primary Mode 로 사용되는 경우 모든 변경 작업이 1개의 Primary 서버를 통해 수행되기 때문에 위와 같은 문제가 발생하지 않습니다.
MySQL 8.0의 원자적(atomic) DDL 지원에 대한 자세한 내용과 특정 명령문의 복제에 대한 동작의 결과 변경 사항은 다음 공식 Document 를 추가로 확인 해보시기 바랍니다.
버전 호환성
최적의 호환성과 성능을 위해 그룹의 모든 구성원이 동일한 버전의 MySQL Server 에서 그룹 복제를 실행해야 합니다. 다중 기본 모드에서는 모든 구성원이 일반적으로 읽기-쓰기 모드가 가능 하기 때문에 이것이 매우 중요한 부분이 됩니다.
그룹에 두 개 이상의 MySQL Server 버전을 실행하는 구성원이 포함된 경우, 일부 구성원은 다른 구성원이 지원하지 않는 기능을 지원하거나 다른 구성원이 갖고 있는 기능이 없어서(부족하기 때문에) 다른 구성원과 호환되지 않을 수 있습니다. 이를 방지하기 위해 새 구성원이 그룹에 가입할 때(업그레이드 및 다시 시작한 이전 구성원 포함) 해당 구성원은 나머지 그룹원에 대해 호환성 검사를 수행합니다.
이러한 검사 결과는 다중 프라이머리 모드 에서는 특히 중요한 체크 사항이며 아래의 내용으로 참여 가능 여부 및 설정을 달리하게 됩니다.
- 새롭게 가입을 하려는 구성원이 기존 그룹의 구성원이 실행 중인 가장 낮은 버전보다 더 낮은 MySQL Server 버전을 실행 중인 경우 해당 새롭게 가입하려는 구성원은 그룹에 가입할 수 없습니다.
- 새롭게 가입하려는 구성원은 기존 그룹 구성원이 실행 중인 가장 낮은 버전과 동일한 MySQL Server 버전을 실행 중인 경우 일반적으로 그룹에 가입할 수 있습니다.
- 그룹에 가입하려는 새로운 구성원이 기존 그룹 구성원이 실행 중인 가장 낮은 MySQL Server 버전 보다 높은 MySQL Server 버전을 사용할 경우 그룹에 가입이 되지만 그룹에 가입 이후 읽기 전용 모드를 유지하게 됩니다.
이 동작은 그룹이 Multi Primary Mode 에서 실행 중일 동작이며, 그룹이 Single Primary Mode 로 동작하는 경우 새롭게 추가되는 구성원은 당연히 기본적으로 어떤 경우에도 읽기 전용으로 참여 됩니다. - MySQL 8.0.17 이상을 실행하는 구성원은 호환성을 확인할 때 릴리스의 패치 버전을 고려합니다. MySQL 8.0.16 이하 또는 MySQL 5.7을 실행하는 멤버는 메이저 버전만 고려합니다.
- Multi Primary Mode 에서 MySQL 8.0.17 이상을 실행하는 구성원은 읽기-쓰기 및 읽기 전용 상태를 자동으로 관리(전환) 합니다.
위와 같이 버전 간의 호환성을 확인하여 동작을 하며, 그룹에서 구성원이 탈퇴를 하게 되면 이를 감지하고 버전을 다시 확인하며, 현재 가장 낮은 버전을 실행하는 구성원은 자동으로 읽기-쓰기 모드로 설정이 됩니다.
group_replication_switch_to_multi_primary_mode() 함수를 사용하여 단일 프라이머리 모드에서 실행 중이던 그룹을 다중 프라이머리 모드로 변경하면 그룹 복제는 자동으로 버전 호환성에 따라서 구성원에 대해서 맞는 모드로 재설정 합니다. 즉 구성원 중에서 그룹에 있는 가장 낮은 버전보다 더 높은 MySQL 서버 버전을 실행 중인 경우 해당 구성원은 자동으로 읽기 전용 모드로 전환되며, 가장 낮은 버전을 실행하는 구성원은 읽기-쓰기 모드로 전환됩니다.
트랜잭션 일관성 보장
그룹 복제와 같은 분산 시스템의 주요 의미 중 하나는 그룹으로 제공되는 일관성 보장입니다. 즉, 그룹 구성원 간에 분산된 트랜잭션의 전역 동기화 일관성입니다. 그룹 복제가 그룹에서 발생하는 이벤트에 따라 일관성 보장을 처리하는 방법과 그룹의 일관성 보장을 가장 잘 구성하는 방법에 대해 확인해보도록 하겠습니다.
트랜잭션 일관성 보장 이해
그룹 복제는 분산 환경에서 정상 또는 장애 상황에서도 항상 최종적으로 일관성 있는 시스템이 되도록 하고 있습니다. 시스템의 일관성과 관련된 이벤트는 수동 또는 장애에 의해 자동으로 트리거 되는 제어 작업으로 분할 되게 됩니다.
그룹 복제에서 각 구성원(멤버)들은 모두 같은 트랜잭션이 수행 및 적용되지만, 실제로 적용되는 시점이나 일치하는 시점은 다를 수 있습니다. 그래서 구성원이 쓰기를 수행한 후 다른 멤버의 인스턴스에서 조회를 수행을 하게 되었을 때 해당 데이터는 최신 시점의 데이터가 아닐 수도 있습니다.
일관성 보장 및 기본 장애 조치
Single Primary Mode 에서 Secondary 인스턴스가 기본으로 승격될 때 Primary 에 대한 장애 조치(Failover)가 발생하는 경우 복제 백로그의 크기에 관계없이 즉시 새로운 Primary 인스턴스에 애플리케이션 트래픽에 사용할 수 있게 할 수 있습니다. 또는 복제 백로그가 적용되거나 적용될 때까지 이에 대한 액세스를 제한할 수도 있습니다.
기본적인 접근 방식으로는 그룹이 새로운 Primary 서버를 선택한 다음 이전 Primary 의 백로그를 적용하는 동안 즉시 데이터 액세스를 허용하는 방식으로 쓰기 일관성이 보장되지만 신규 Primary 에서 이전의 백로그를 적용하는 동안에 발생되는 트랜잭션은 오래된 이전 데이터를 읽거나 쓸 수 있게 될 수도 있습니다.
일반 적인 서버의 상태 또는 트랜잭션의 상태에서는 구성원 간의 복제나 데이터 동기화가 늦지 않고 빠르게 적용되기 때문에 위와 같은 현상은 잘 발생되지 않으나 동기화가 빠르게 처리되는 과정에서도 Failover 나 일시적인 문제 등으로 짧은 순간에 발생될 수는 있으며 이전의(오래된) 데이터를 읽을 수도 있습니다.
MySQL 8.0.14 이전에는 장애 조치 정책을 구성하는 방법이 따로 있지 않았으며 위에서 설명한 기본적인 접근 방식으로 가용성을 최대화하였습니다. MySQL 8.0.14 이상을 실행하는 구성원이 있는 그룹에서는 group_replication_consistency 시스템 변수를 사용하여 기본 장애 조치 중에 구성원이 제공하는 트랜잭션 일관성 보장 수준을 구성할 수 있습니다. 관련된 내용은 다음 "트랜잭션 일관성 보장 구성" 에서 더 자세하게 설명하도록 하겠습니다.
트랜잭션 동기화 포인트
그룹 전체에서 트랜잭션을 동기화하려는 지점을 기준으로 그룹의 일관성 보장을 구성합니다.
개념을 이해하는 데 도움이 되도록 이번 섹션에서는 그룹 전체에서 트랜잭션을 동기화하는 지점을 읽기 작업 시 또는 쓰기 작업 시로 단순화하도록 하겠습니다.
읽을 때 데이터가 동기화되면 현재 클라이언트 세션은 실행을 시작하기 전에 이전의 모든 업데이트 트랜잭션이 적용된 특정 시점까지 기다리게 됩니다. 이 접근 방식을 사용하면 이 세션만 영향을 받고 다른 모든 동시 데이터 작업은 영향을 받지 않습니다.
쓰기 시 데이터가 동기화가 되면 쓰기 세션은 그룹내 모든 Secondary 서버가 데이터를 쓸 때까지 기다리게 됩니다. 그룹 복제는 쓰기에 대한 전체 순서를 사용하므로 Secondary 서버의 대기열(queues)에 있는 이전의 모든 쓰기 트랜잭션이 적용될 때까지 기다리는 것을 의미합니다. 따라서 이 동기화 지점을 사용할 때 쓰기 세션은 그룹내 모든 Secondary 의 queues 가 적용될 때까지 기다립니다.
다음 예에서는 두가지 유형의 작업 부하를 설명하고 적절한 동기화 지점을 알려줍니다.
주로 읽기 전용 데이터가 있는 그룹이 있는 경우 읽기-쓰기 트랜잭션이 커밋되면 모든 곳에 적용되어 최신 쓰기가 포함된 최신 데이터에 대해 후속 읽기가 수행되기를 원할 경우, 모든 RO 트랜잭션에 대해 동기화 비용을 지불하지 않고 RW 트랜잭션에 대해서만 동기화 비용을 지불할 수 있습니다.
-> 이러한 경우 쓰기 시 동기화를 선택해야 합니다.
예를 들어 민감한 데이터가 업데이트될 때마다(예: 파일 또는 유사한 데이터에 대한 자격 증명) 항상 그룹에서 최신 데이터를 읽는 트랜잭션을 원하고 해당 읽기가 최대한 최신 정보를 검색하도록 강제하고 싶습니다.
-> 이러한 경우 읽기에서 동기화하도록 선택해야 합니다.
트랜잭션 일관성 보장 구성
위의 내용에서 개념적으로 선택할 수 있는 두 가지 동기화 지점이 있다고 설명하였지만 읽기 또는 쓰기 시 이러한 용어는 단순화하여 설명된 내용입니다. 그룹 복제에 사용되는 용어는 트랜잭션 실행 전과 후입니다.
일관성 수준은 아래에서 설명할 내용 대로 그룹에서 처리하는 읽기 전용(RO) 및 읽기-쓰기(RW) 트랜잭션에 서로 다른 영향을 미칠 수 있습니다.
위에서 한번 설명한 내용과 같이 MySQL 8.0.14 이전에는 장애 조치 정책을 구성하는 방법이 따로 있지 않았으며 위에서 설명한 기본적인 접근 방식으로 가용성을 최대화하였습니다.
MySQL 8.0.14 버전 이상을 실행하는 구성원이 있는 그룹에서는 group_replication_consistency 시스템 변수를 사용하여 기본 장애 조치 중에 구성원이 제공하는 트랜잭션 일관성 보장 수준을 구성할 수 있습니다. 해당 시스템 변수의 영향은 읽기 전용(RO) 및 읽기 쓰기(RW) 트랜잭션 모두에 대해 고려되어야 합니다.
해당 시스템 변수의 기본 값은 EVENTUAL 이며 글로벌 또는 세션 범위로 적용할 수 있습니다. 해당 시스템 변수를 통해서 원하는 수준의 일관성을 선택하여 운영할 수 있으며 group_replication_consistency 시스템 변수에 설정할 수 있는 값에 대해서 일관성 수준에 대해서 확인해 보도록 하겠습니다.
EVENTUAL
EVENTUAL 일관성 수준은 group_replication_consistency 시스템 변수의 기본 값이며, 해당 시스템 변수가 도입되기 전에 사용하였던 기본적인 일관성 수준과 내용은 동일 합니다.
RO 및 RW 트랜잭션은 실행 전에 선행 트랜잭션이 적용 완료를 기다리지 않습니다. 즉 EVENTUAL 일관성 에서는 트랜잭션의 수행에 대해서 제약이 없이 바로 실행이 가능 합니다.
RW 트랜잭션은 다른 구성원이 트랜잭션을 적용할 때까지 기다리지 않습니다. 이는 트랜잭션이 다른 구성원보다 먼저 외부화(externalized)될 수 있음을 의미합니다.
이는 또한 기본 장애 조치가 발생하는 경우 이전 기본 트랜잭션이 모두 적용되기 전에 새로운 Primary 에 새로운 트랜잭션 RO 및 RW 트랜잭션을 수락할 수 있음을 의미합니다.
이 과정에서 RO 트랜잭션은 이전의(오래된) 값을 읽을 수도 있으며, RW 트랜잭션은 충돌로 인해 롤백을 초래할 수 있습니다.
BEFORE_ON_PRIMARY_FAILOVER
아직 이전 Primary에서 백로그를 적용하고 있는 경우 새로 선출된 Primary 로 요청되는 새로운 RO 또는 RW 트랜잭션은 새로운 Primary 에서 이전 Primary 의 백로그가 모두 적용될 때까지 보류(적용되지 않음)됩니다.
이렇게 하면 의도적이든 아니든 Primary 서버의 장애 조치가 발생할 때 클라이언트가 항상 최신 값을 볼 수 있습니다. 이는 일관성을 보장하지만 백로그가 적용하고 이는 경우 완료하기까지 클라이언트의 요청은 지연이 될 수 있음을 의미합니다. 일반적으로 이 지연은 최소화해야 하지만, 지연에 대한 정도는 새로운 Primary 에 적용해야 할 백로그의 크기에 따라 다릅니다.
보통의 경우(트랜잭션이 적었거나 복제의 Gap 등이 없이 원만하게 사용중인 경우) 트랜잭션 갭이 크지 않을 것이므로 지연의 정도가 크지 않을 것이지만, 예상치 못한 장애 상황이나 문제 등으로 복제의 Gap이 크게 될 경우 트랜잭션의 처리 시간이 길어 짐에 따라 클라이언트가 지연(응답이 길어지는) 이 발생할 수 있습니다.
이와 같은 일관성은 Single Primary Mode 사용 중에서 문제가 발생되어 새로운 Primary 가 선출되는 상황에서 적용되는 일관성 수준입니다.
BEFORE_ON_PRIMARY_FAILOVER 는 새로운 Primary 서버에 대한 새로운 트랜잭션이 발생되면 이전 Primary의 백로그가 완전히 적용될 때까지 들어오는 트랜잭션이 차단됩니다. 그에 따라서 다음과 같은 이상이 방지됩니다.
- 신규 Primary 로 요청되는 읽기 전용 및 읽기-쓰기 트랜잭션에 대해서 오래된 데이터가 아닌 최신의 데이터를 바탕으로 읽고 쓰기 작업을 할 수 있습니다.
- 신규 Primary 로 요청된 읽기-쓰기 트랜잭션과 여전히 적용 중인 이전 Primary의 백로그에서 복제된 읽기-쓰기 트랜잭션과의 충돌에 의한 spurious rollbacks 이 없어지게 됩니다. 즉 충돌이 발생하지 않게 됩니다.
위에서 설명한 내용과 같이 BEFORE_ON_PRIMARY_FAILOVER 일관성 수준을 사용할 경우 신규 Primary 에서 이전 Primary 의 백로그를 적용하고 있을 경우, 새로운(신규) Primary 로 요청된 읽기-쓰기(Read-Write,RW) 트랜잭션은 처리가 지연되지만, 모든 읽기 작업이 차단 및 지연되는 것은 아닙니다.
디버깅, 모니터링 등을 위한 다음과 같은 데이터를 수정하지 않는 일부 쿼리가 허용되게 됩니다.
-
SHOW
statements - from MySQL 8.0.27 this is restricted to those that do not depend on data, only on status and configuration, as listed below -
SET
statements -
DO
statements that do not use tables or loadable functions -
EMPTY
statements -
USE
statements -
Using
SELECT
statements against theperformance_schema
andsys
databases -
Using
SELECT
statements against thePROCESSLIST
table from theinfoschema
database -
SELECT
statements that do not use tables or loadable functions -
STOP GROUP_REPLICATION
statements -
SHUTDOWN
statements -
RESET PERSIST
statements
참고) MySQL 8.0.27에서 허용되는 SHOW 문
SHOW VARIABLES, SHOW PROCESSLIST, SHOW STATUS, SHOW ENGINE INNODB LOGS, SHOW ENGINE INNODB STATUS, SHOW ENGINE INNODB MUTEX, SHOW MASTER STATUS, SHOW REPLICA STATUS, SHOW CHARACTER SET, SHOW COLLATION, SHOW BINARY LOGS, SHOW OPEN TABLES, SHOW REPLICAS, SHOW BINLOG EVENTS, SHOW WARNINGS, SHOW ERRORS, SHOW ENGINES, SHOW PRIVILEGES, SHOW PROCEDURE STATUS, SHOW FUNCTION STATUS, SHOW PLUGINS, SHOW EVENTS, SHOW PROFILE, SHOW PROFILES, and SHOW RELAYLOG EVENTS.
BEFORE
BEFORE 일관성에서는 RW 트랜잭션 과 RO 트랜잭션은 적용되기 전에 모든 선행 트랜잭션이 완료될 때까지 기다립니다.
이렇게 하면 트랜잭션은 다른 트랜잭션의 대기 시간에만 영향을 주게 되고 트래잭션은 항상 최신 값을 읽도록 되게 됩니다. 이는 동기화가 RO 트랜잭션에서만 사용되도록 하여 모든 RW 트랜잭션에서 동기화 오버헤드를 줄입니다.
선행 트랜잭션이 최종적으로 적용되는 시간만큼 영향을 받게 되므로 선행 트랜잭션이 짧다면 자신의 처리 시간만 고려하면 되게 됩니다.
이 일관성 수준에는 BEFORE_ON_PRIMARY_FAILOVER에서 제공하는 일관성 보장도 포함됩니다.
AFTER
AFTER 일관성은 RW 트랜잭션의 변경 사항이 다른 모든 구성원에게 적용될 때까지 기다립니다. 이 값은 RO 트랜잭션에 영향을 미치지 않습니다.
이 모드는 트랜잭션이 로컬 구성원에 대해 커밋 될 때 모든 후속 트랜잭션이 그룹 구성원에 대해 기록된 값 또는 최신 값을 갖도록 합니다.
적용된 RW 트랜잭션이 커밋되면 모든 곳에 적용되도록 하려면 주로 RO 작업에 사용되는 그룹과 함께 이 모드를 사용합니다. 이것은 후속 읽기가 최신 쓰기를 포함하는 최신 데이터를 가져오도록 하기 위해 애플리케이션에서 사용할 수 있습니다.
이것은 동기화가 RW 트랜잭션에서만 사용되도록 하며, 모든 RO 트랜잭션에서 동기화 오버헤드를 줄입니다.
다만 다른 구성원에서 트랜잭션이 모두 완료되어야 하기 때문에 각 서버 별로 트랜잭션을 처리하던 것에 비해서 더 많은 처리 시간을 소비하게 됩니다. 쓰기의 비중이 높지 않거나 RO 에 비해서 많이 낮으며, 쓰기 직후 읽기 작업시 항상 최신의 데이터를 읽어야 하는 경우에 적합하다고 할 수 있습니다.
이 일관성 수준에는 BEFORE_ON_PRIMARY_FAILOVER에서 제공하는 일관성 보장도 포함됩니다.
BEFORE_AND_AFTER
BEFORE_AND_AFTER 일관성에서의
RW 트랜잭션은
1) 적용되기 전에 완료해야 하는 이전의 모든 트랜잭션을 기다리게 되고
2) 변경 사항이 다른 구성원에 적용될 때까지 기다립니다.
RO 트랜잭션은 실행이 일어나기 전에 모든 선행 트랜잭션이 완료될 때까지 기다립니다.
즉, BEFORE 와 AFTER 가 결합된 형태입니다.
이 일관성 수준에는 BEFORE_ON_PRIMARY_FAILOVER에서 제공하는 일관성 보장도 포함됩니다.
BEFORE 및 BEFORE_AND_AFTER 일관성 수준은 RO 및 RW 트랜잭션에서 모두 사용할 수 있습니다. AFTER 일관성 수준은 변경 사항을 생성하지 않기 때문에 RO 트랜잭션에 영향을 미치지 않습니다.
이번 포스팅은 여기에서 정리하도록 하겠으며 아래 포스팅에서 계속됩니다.
Reference
Reference URL
• mysql.com/group-replication-deploying-in-multi-primary-or-single-primary-mode
• mysql.com/group-replication-multi-primary-mode
• mysql.com/group-replication-online-upgrade-combining-versions
• mysql.com/group-replication-understanding-consistency-guarantees
• mysql.com/group-replication-preventing-stale-reads-on-primary-fail-over
연관된 다른 글
Principal DBA(MySQL, AWS Aurora, Oracle)
핀테크 서비스인 핀다에서 데이터베이스를 운영하고 있어요(at finda.co.kr)
Previous - 당근마켓, 위메프, Oracle Korea ACS / Fedora Kor UserGroup 운영중
Database 외에도 NoSQL , Linux , Python, Cloud, Http/PHP CGI 등에도 관심이 있습니다
purityboy83@gmail.com / admin@hoing.io