포스트

MaxScale GTID 복제 중단 트러블슈팅 (feat. 내가 해결하려 했지만...)

MaxScale GTID 복제 중단 트러블슈팅 (feat. 내가 해결하려 했지만...)

시스템 구성

VIP 192.168.0.10

  • 가상 IP, Active/Standby 전환 시 자동으로 Active DB를 가리킴

Master DB 192.168.0.11

  • 쓰기 전담

Slave DB 192.168.0.12

  • 읽기 전담 (복제 대상)

MaxScale의 readwritesplit 설정으로 Write는 Master, Read는 Slave로 자동 분리되는 구조다.


문제 발생

인지 시점

  • 2026-03-26 백오피스 데이터 조회 중 이상 현상 발견

증상

증상 1. IDE에서 INSERT 후 백오피스에 데이터 미반영

1
2
IDE 툴에서 DBM(192.168.0.10)으로 직접 INSERT
→ 백오피스 화면에서 해당 데이터 조회 불가

증상 2. 백오피스에서 INSERT 후 화면에 미표시

1
2
3
백오피스에서 데이터 INSERT
→ DB 직접 조회 시엔 데이터 존재
→ 백오피스 화면에서는 데이터 미표시

원인 분석

MaxScale 로그 확인

1
tail -n 100 /var/log/maxscale/maxscale.log

로그에서 결정적인 내용 발견:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 3월 12일 기록
2026-03-12 11:18:57 notice :
Server changed state: server2[192.168.0.12:3306]: lost_slave.
[Slave, Running] -> [Running]
SQL Running: No
Error: An attempt was made to binlog GTID 0-10-1000001
which would create an out-of-order sequence number
with existing GTID 0-11-1000000,
and gtid strict mode is enabled

# 오늘 기록
2026-03-26 16:46:52 notice : Updated 'passive' from 'false' to 'true'
2026-03-26 16:47:00 notice : Updated 'passive' from 'true' to 'false'

GTID란?

GTID(Global Transaction ID)는 DB에서 발생하는 모든 트랜잭션에 부여되는 전역 고유 식별자다.

1
2
형식: 도메인ID - ServerID - SequenceNumber
예시: 0 - 11 - 1000000
  • 0 → 도메인 ID
  • 11 → 발생 서버 ID
  • 1000000 → 트랜잭션 순번

트랜잭션이 COMMIT 되는 순간 생성되며, Slave는 이 번호를 기반으로 어디까지 복제했는지 추적한다.

1
2
3
4
정상 복제 흐름:
Master COMMIT → GTID 발급 → Binary Log 기록
→ Slave IO Thread가 감지 → Relay Log로 복사
→ Slave SQL Thread가 순서대로 실행 → 데이터 반영

왜 충돌이 발생했나

1
2
3
4
5
정상: Master(server_id=11) 에서만 GTID 발급
      0-11-1000000

실제: Slave(server_id=10) 에도 GTID가 생성됨
      0-10-1000001  ← Slave에 직접 쓰기 발생 증거

MaxScale이 불안정한 상태(passive 전환)에서 쓰기 쿼리가 Slave로 라우팅된 것으로 추정된다.

gtid_strict_mode = ON 상태에서 순서가 어긋난 GTID는 데이터 정합성 보호를 위해 복제 자체를 거부한다.

결과적으로 3월 12일부터 Slave 복제가 중단된 채 운영 중이었다.

증상과 연결

IDE INSERT → 백오피스 미반영

  • MaxScale이 IDE 세션을 Slave로 라우팅 (서로 다른 DB)

백오피스 INSERT → 화면 미표시

  • Write는 Master 저장, Read는 복제 멈춘 Slave 조회

해결 방법 (내가 파악한 것)

즉시 조치

1
2
3
# Slave를 MaxScale 서비스에서 격리
# Read 쿼리가 복제 멈춘 Slave로 가지 않도록 차단
maxctrl set server server2 maintenance

복제 재동기화 옵션

GTID 강제 스킵

  • 충돌 GTID만 건너뜀
  • 적합: 충돌 건수 적을 때

mysqldump

  • Master 데이터 전체 재주입
  • 적합: DB 용량 작을 때

mariabackup

  • 무중단 백업 후 재동기화
  • 적합: 운영 환경 권장
    1
    2
    3
    4
    5
    
    -- GTID 강제 스킵 예시 (Slave에서 실행)
    STOP SLAVE;
    SET GLOBAL gtid_slave_pos = '0-11-1000000';
    START SLAVE;
    SHOW SLAVE STATUS\G
    

해결하려 했지만…

원인도 파악했고, 해결 방법도 정리했다. maxctrl 명령어로 Slave 격리 후 복제 재동기화까지 진행할 수 있는 상황이었다.

근데 부장님이 막았다.

“너는 DBA가 아니잖아” “전문가도 아닌데 왜 건드려” “유지보수 업체 있는데 걔네한테 맡겨”

틀린 말은 아니다. 운영 DB 작업은 실수 한 번이 서비스 전체 장애로 이어질 수 있다. 부장님 입장에선 리스크 관리 차원에서 당연한 판단이다.

근데 솔직히 답답했다. MaxScale 로그도 직접 분석했고, GTID 충돌 원인도 특정했고, 해결 방법도 3가지나 파악해놨다. 우리 시스템 구조를 가장 잘 아는 사람이 나인데, 유지보수 업체가 와도 결국 내가 정리한 분석 내용 기반으로 작업하게 될 텐데.

결국 내가 한 건 분석 내용을 문서로 정리해서 유지보수 업체에 전달하는 것이었다. 직접 손은 못 댔지만, 원인 특정과 해결 방향은 내가 잡았다.


마무리

MaxScale 로그는 평소에 확인하는 습관을 들여야 한다. 이번 건은 복제 중단이 2주나 방치됐다. Slave에 직접 쓰기가 들어가면 GTID가 꼬이고, gtid_strict_mode가 켜져 있으면 데이터 정합성을 지키는 대신 복제를 아예 멈춰버린다.

그리고 하나 더 느낀 건, 기술적으로 할 수 있다는 것과 조직에서 해도 되는 건 별개라는 거다. 결국 문서화를 잘 해두는 게 중요하다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.