운영 DB의 시간당 백업을 대기 서버로 보내고 있었다. 최근 24시간의 수신 기록에서 대기 서버가 가진 최신 스냅샷의 나이는 최대 1시간 29초였다. 그때 원본 서버를 잃었다면 스냅샷 이후에 저장한 기록은 백업에서 복구할 수 없었다. 전송이 밀리면 이 구간은 더 길어질 수 있다.
대기 서버에 MySQL 복제본을 두면 백업 사이의 변경도 따라갈 수 있다. 그러자 다른 질문이 생겼다. 두 서버 사이 연결만 끊겼을 때, 아직 정상인 원본은 계속 저장을 받아야 할까?
트랜잭션이 어디까지 갔는지 확인하기
비동기 복제에서 원본은 복제본의 수신을 기다리지 않고 커밋 성공을 응답할 수 있다. 장애를 준비할 때는 원본이 성공을 알린 트랜잭션, 복제본이 받은 트랜잭션, 복제본이 적용한 트랜잭션을 구분해야 한다. 복제본이 이미 받은 분량은 쓰기를 열기 전에 적용을 마칠 수 있다. 아직 받지 못한 분량을 가져오려면 원본에 접근할 수 있어야 한다. MySQL의 GTID 기반 장애 전환
격리 리허설에서는 복제본의 적용만 잠시 멈췄다. 그 사이 원본에서 커밋된 20건이 복제본에 수신된 것을 확인했고, 기존 원본의 쓰기를 차단한 뒤 20건을 모두 적용해 승격했다. 수신까지 끊은 별도 리허설에서는 원본이 성공을 반환한 30건이 복제본에 없었다. 두 경우는 트랜잭션 식별자(GTID)와 실제 저장된 행을 대조해 구분했다.
같은 단절에서 쓰기 차단을 비교하기
한 호스트에 MySQL 8.4.13 인스턴스 두 개를 격리해 띄우고 테스트 쓰기 요청을 같은 목표 주기로 보냈다. 복제 연결을 5초간 끊고, 이미 처리 중이던 요청의 응답까지 기록했다. 쓰기를 계속 받는 경우는 한 번, 차단하는 경우는 세 번 실행했다.
차단하는 쪽은 원본이 발행한 확인 행을 복제본에서 읽었다. 마지막으로 적용이 확인된 행의 발행 시각에서 1초가 지나면 쓰기 계정을 잠그고 열린 쓰기 세션을 종료한 뒤 super_read_only를 켰다. 계정 잠금은 원본에만 적용했다. 복제가 따라잡은 뒤에도 명시적으로 열기 전까지 쓰기는 계속 거부됐다. MySQL의 읽기 전용 설정
| 시험 | 단절 후 복제되지 않은 마지막 성공 응답 | 성공했지만 복제본에 없던 쓰기 | 전체 쓰기 차단 확인 |
|---|---|---|---|
| 쓰기 유지 | 5.090초 | 47건 | 시험 구간에 쓰기 거부 0건 |
| 차단 1회차 | 0.674초 | 5건 | 1.373초 |
| 차단 2회차 | 0.849초 | 5건 | 1.482초 |
| 차단 3회차 | 0.658초 | 4건 | 1.450초 |
단절부터 마지막 미복제 성공 응답까지 걸린 시간은 5.090초에서 최대 0.849초로 줄었다. 복제본이 받기 전에 원본까지 잃으면 표에 적힌 쓰기가 유실될 수 있다. 실험에서는 연결을 복구한 뒤 양쪽 데이터가 일치했다. 열린 장기 트랜잭션의 세션을 끊었을 때는 커밋되지 않은 행이 양쪽 DB에 남지 않았다.
여기서 1초는 확인 신호의 유효기간이다. 표에서 보듯 전체 차단까지는 계정 잠금, 기존 연결 종료, 읽기 전용 전환에 시간이 더 든다. 실험은 같은 호스트에서 했으므로 실제 두 서버 사이의 지연과 감시기 전체 중단, 오래 걸리는 롤백을 포함한 유실 상한은 별도로 검증해야 한다.
운영에서는 경보를 먼저 적용했다
저는 초기 운영 정책으로 복제 단절 시에도 원본이 저장을 계속하고, 운영자가 경보를 확인하는 방식을 선택했다. 자동으로 쓰기를 멈추면 잠깐의 서버 간 연결 장애만으로도 로그인 세션, 녹음 업로드 기록, 채점 결과 저장이 중단될 수 있기 때문이다. 이 선택으로 저장 기능을 유지하는 대신, 단절 중 원본까지 고장 나면 복제본이 받지 못한 기록을 잃을 위험을 감수한다.
운영에는 읽기 전용 복제본과 상태 경보를 적용했다. 초기 사본은 데이터 해시를 대조했고, 그 뒤 원본에서 생긴 트랜잭션까지 적용되는 것을 확인했다. 복제 수신을 잠깐 멈췄을 때 대기 서버의 상태 점검에 실패가 표시됐고, 수신을 재개하자 정상으로 돌아왔다.
DB 승격과 사용자 요청 전환을 따로 재기
앱만 멈추고 프록시와 주 DB가 살아 있다면 대기 앱으로 요청을 보내면서 기존 주 DB를 쓸 수 있다. 프록시와 주 DB가 함께 멈추면 사용자 접속 경로와 쓰기 DB를 각각 복구해야 한다. 복제본을 승격하기 전에는 기존 원본이 다시 쓰기를 받지 못하게 막고, 원복 전에는 승격한 DB에서 받은 쓰기를 기존 원본에 반영해야 한다.
별도의 백업 복원 리허설에서는 장애 선언부터 내부 HTTPS 경로 전환과 로그인·새 쓰기 성공까지 32.82초, 녹음 업로드와 채점 결과 조회까지 34.28초가 걸렸다. 이 시간은 사용자가 다시 기능을 쓸 수 있기까지의 측정값이다. 복구할 수 있는 마지막 데이터 시점은 스냅샷이나 복제본의 수신 기록으로 따로 확인했다. 운영 도메인의 DNS와 공인 인증서를 포함한 전환 시간은 아직 측정하지 않았다.

댓글
댓글과 반응은 GitHub 계정으로 로그인해 남길 수 있어요.