Skip to content
난이도 면접 빈출문제은행 3

바이너리 로그 포맷과 동기화 방식 ​

복제(replication)의 품질은 두 가지 선택으로 정해진다. 첫째는 바이너리 로그(binary log)에 무엇을 적느냐다. 실행한 SQL 문장을 적을 수도 있고 바뀐 행을 적을 수도 있다. 둘째는 소스(source)가 커밋(commit)을 끝내기 전에 레플리카(replica)를 얼마나 기다리느냐다. 앞의 선택은 소스와 레플리카의 데이터가 어긋날 위험을, 뒤의 선택은 소스 장애 때 잃을 수 있는 데이터의 범위를 좌우한다. 이 문서는 로그 포맷과 기본 동작인 비동기 복제(asynchronous replication)를 다루고, 반동기 복제(semisynchronous replication)는 바이너리 로그 포맷과 동기화 방식 2에서 이어서 본다.

바이너리 로그 포맷 세 가지 ​

binlog_format이 기록 방식을 정한다.

  • STATEMENT: 변경을 일으킨 SQL 문장 자체를 적는다. 이를 쓰는 복제를 문장 기반 복제(statement-based replication)라 한다. 레플리카는 같은 문장을 다시 실행한다. 행 100만 개를 지우는 DELETE도 한 줄로 기록되어 로그가 작다. 하지만 실행할 때마다 결과가 달라지는 문장은 소스와 레플리카에서 다른 결과를 낸다. UUID(), RAND(), SYSDATE() 같은 비결정적 함수(nondeterministic function)를 쓰는 문장이 그런 예다. ORDER BY 없이 LIMIT을 건 UPDATE·DELETE도 어떤 행이 먼저 읽힐지 정해져 있지 않아, 소스와 레플리카가 서로 다른 행을 고칠 수 있다.
  • ROW: 문장 대신 바뀐 행의 값을 적는다. 이를 행 기반 복제(row-based replication)라 한다. 레플리카는 문장을 다시 계산하지 않고 행을 그대로 반영하므로 결과가 항상 같다. 대신 많은 행을 바꾸는 문장은 행 수만큼 로그가 커진다.
  • MIXED: 평소에는 문장으로 적다가, 결과가 달라질 수 있는 안전하지 않은 문장이면 서버가 그 문장만 행 형식으로 바꿔 적는다.
비교STATEMENTROW
기록 대상SQL 문장바뀐 행의 전·후 값
로그 크기작다대량 변경이면 크다
소스와 결과 일치비결정적 함수에서 깨질 수 있음항상 같음
레플리카 부담무거운 문장을 다시 실행행만 반영
잠금INSERT ... SELECT 등이 더 많이 잠금더 적음

STATEMENT에서는 레플리카가 문장을 다시 실행해 같은 결과를 내야 하므로, 소스는 INSERT ... SELECT가 읽는 원본 행까지 잠가 그 사이의 변경을 막는다. ROW는 결과 행을 그대로 보내므로 이런 잠금이 덜 필요하다.

MySQL 8.4의 기본값은 ROW다. 더 나아가 8.4 매뉴얼은 binlog_format 변수 자체를 폐기 예정(deprecated)으로 표시하고, 행 기반이 아닌 포맷 지원도 앞으로 없어질 수 있으니 새 복제 구성에는 행 기반만 쓰라고 권고한다. 그룹 복제(Group Replication)처럼 행 형식이 필수인 기능도 있다. 그래서 실무에서 포맷을 고르는 문제는 사실상 "ROW를 쓰고 로그 크기를 어떻게 줄이느냐"가 된다.

행 이미지 크기: binlog_row_image ​

행 기반 로그에 칼럼을 얼마나 적을지는 binlog_row_image가 정한다. 행 기반 로그의 UPDATE 이벤트(바이너리 로그에 적히는 변경 기록 한 건)에는 두 벌의 값이 들어간다. 바꾸기 전 값인 전 이미지(before image)는 레플리카가 고칠 행을 찾는 데 쓰고, 바꾼 뒤 값인 후 이미지(after image)는 바꿀 값을 담는다. DELETE는 전 이미지만, INSERT는 후 이미지만 적는다.

값기록하는 칼럼
full (기본)전·후 이미지 모두 모든 칼럼
minimal전 이미지는 행을 찾는 데 필요한 칼럼, 후 이미지는 문장이 값을 지정했거나 AUTO_INCREMENT가 만든 칼럼
noblob모든 칼럼. 단 행을 찾는 데 필요 없고 바뀌지도 않은 BLOB·TEXT 칼럼은 뺀다

minimal에서 "행을 찾는 데 필요한 칼럼"은 테이블에 PK(Primary Key, 기본 키)가 있으면 PK 칼럼뿐이다. PK가 없고 모든 칼럼이 NOT NULL인 유니크 인덱스(unique index)도 없으면 모든 칼럼을 적어야 한다. minimal은 로그를 크게 줄이지만, 소스와 레플리카의 테이블 칼럼 구성과 PK 정의가 같다는 전제가 깨지면 레플리카가 엉뚱한 행을 고치고도 오류 없이 데이터가 어긋날 수 있다. 레플리카는 로그에 적힌 칼럼 값과 맞는 행을 찾으면 정상으로 보고 고치기 때문이다. PK가 없는 테이블은 레플리카가 바뀐 행을 바로 짚을 키가 없어 테이블을 훑게 되므로 복제 지연(replication lag)의 대표 원인이 된다. 그래서 행 기반 복제를 쓰면 모든 테이블에 PK를 두는 것이 기본이다.

비동기 복제: 기본 동작 ​

MySQL 복제는 기본적으로 비동기 복제다. 소스는 트랜잭션(transaction)을 바이너리 로그에 적고 커밋한 뒤 곧바로 클라이언트에 성공을 돌려준다. 레플리카가 그 이벤트를 받았는지는 확인하지 않는다. 그래서 커밋이 빠르다.

그 대가는 유실 가능성이다. 소스가 커밋 직후 죽으면, 아직 어느 레플리카에도 전달되지 않은 트랜잭션이 있을 수 있다. 이때 장애 조치(failover)로 레플리카를 새 소스로 올리면 클라이언트가 "성공"을 받은 변경이 사라진다.

이어지는 내용은 바이너리 로그 포맷과 동기화 방식 2에서 다룬다.

면접 포인트 ​

  • "STATEMENT와 ROW 차이?" → 문장 vs 바뀐 행. 문장은 작지만 비결정적 함수에서 어긋남, 행은 크지만 항상 같음. 8.4 기본 ROW, binlog_format 자체가 폐기 예정.
  • "binlog_row_image=minimal의 조건?" → 행을 찾을 PK가 있고 양쪽 테이블 구조가 같아야 한다. 아니면 조용히 어긋난다.
  • "비동기 복제의 위험은?" → 소스가 레플리카를 기다리지 않고 커밋하므로, 커밋 직후 소스가 죽으면 성공을 받은 변경도 잃을 수 있다.

관련 문서