버전별 기본값
| 버전 | binlog_format | 비고 |
|---|---|---|
| MySQL 5.6 | STATEMENT | |
| MySQL 5.7.7+ | ROW | 기본값 변경 (Release Notes) |
| MySQL 8.0 | ROW | |
| MySQL 8.4 | ROW | STATEMENT/MIXED deprecated (Refman) |
| MySQL 9.x | ROW |
포맷 변화의 배경
STATEMENT 포맷
실행된 SQL 문장 자체를 binlog에 기록함. UPDATE user SET point = point + 10 같은 대량 쿼리도 한 줄만 기록되므로 로그 크기가 작음.
버려진 원인 — 비결정적(Non-deterministic) 함수 문제. NOW(), UUID(), RAND() 등은 source와 replica에서 실행 시점이 다르므로 값이 달라짐. 또한 READ COMMITTED 격리 수준에서는 데이터 일관성을 보장할 수 없어 REPEATABLE READ 강제가 필요했음.
MIXED 포맷
기본적으로 STATEMENT 방식을 사용하되, MySQL이 “unsafe”하다고 판단한 쿼리는 ROW 방식으로 전환해 기록함. 용량 이점과 정합성을 동시에 노린 타협안이었음.
정착하지 못한 원인. 옵티마이저가 모든 위험 상황을 완벽하게 감지하지 못함. 대표적인 실제 사례:
| 버그/사례 | 버전 | 상황 | 결과 |
|---|---|---|---|
| Bug #73936 | 5.6.20 ~ 8.0.14 | unsafe 쿼리 + ROW 미지원 스토리지 엔진 | STATEMENT로 조용히 기록, 경고 없음 |
MySQL 공식 문서도 이 한계를 인정함. unsafe로 분류되지 않은 함수는 통상적인 안전 조치가 적용되지 않아 에러 로그에 경고가 남지 않고, MIXED 포맷에서도 ROW 방식이 아닌 STATEMENT로 기록됨. (MySQL 8.0 Docs)
unsafe 여부를 MySQL이 “safe”하다고 판단하면 경고조차 없이 STATEMENT로 기록됨. 즉 MIXED는 완전한 신뢰를 줄 수 없음.
ROW 포맷
쿼리가 아닌 변경된 행의 Before/After 이미지를 기록함. 어떤 함수를 써도 source에서 확정된 값 자체가 replica로 복사되므로 데이터 불일치가 사실상 없음.
현재 표준으로 정착한 배경 (추정):
- 완벽한 데이터 정합성 — 비결정적 함수, 격리 수준 차이, 스토리지 엔진 종류에 무관하게 source의 최종 상태가 그대로 전파됨.
- 고가용성(HA) 아키텍처 필수 조건 — MGR(MySQL Group Replication), InnoDB Cluster 등 분산/이중화 환경에서 노드 간 충돌 감지와 완벽한 동기화를 위해 ROW 포맷을 요구함. (Group Replication Requirements)
- CDC 생태계 호환 — Debezium, Kafka Connect, ClickHouse MaterializedMySQL 등 변경 데이터를 외부 시스템에 스트리밍하는 도구들이 실제 변경 값을 필요로 함.
재현 테스트: UUID() 삽입 시 포맷별 동작 비교
테스트 환경: MySQL 8.4.8 LTS, source + replica 단일 채널,
replica_parallel_workers=1
CREATE TABLE t (id INT AUTO_INCREMENT PRIMARY KEY, uuid_val VARCHAR(36)) ENGINE=InnoDB;
INSERT INTO t (uuid_val) VALUES (UUID()), (UUID()), (UUID());| 포맷 | source uuid_val | replica uuid_val | 일치 여부 |
|---|---|---|---|
| STATEMENT | c7019d26-... | c702c2e3-... | ✗ 불일치 |
| MIXED | d86ebf07-... | d86ebf07-... | ✓ 일치 |
| ROW | e9cac9dc-... | e9cac9dc-... | ✓ 일치 |
binlog 이벤트 타입 비교:
| 포맷 | binlog 이벤트 | 의미 |
|---|---|---|
| STATEMENT | Query: INSERT INTO t ... VALUES (UUID()) | SQL 문장 그대로 기록 → replica에서 재실행 |
| MIXED | Table_map + Write_rows | UUID() 감지 → ROW로 자동 전환 |
| ROW | Table_map + Write_rows | 항상 실제 값 기록 |
MIXED가 UUID()는 올바르게 처리하지만, 위 버그 사례처럼 감지 실패 케이스가 남아있음.
ROW 포맷의 단점과 최적화
ROW 포맷의 대표적인 단점은 로그 용량 폭발임. 100만 건 UPDATE 시 100만 개 행의 변경 내역이 모두 기록됨. binlog_row_image 옵션으로 완화할 수 있음.
binlog_row_image 옵션
MySQL 5.6부터 도입. 변경 Row의 어느 범위를 기록할지 제어함.
| 값 | 기록 내용 | 복제 적합성 | CDC 연동 |
|---|---|---|---|
FULL (기본값) | 전체 컬럼 | ✓ | ✓ 필수 |
MINIMAL | PK + 변경된 컬럼만 | ✓ | ✗ |
NOBLOB | FULL에서 미변경 BLOB/TEXT 제외 | ✓ | ✗ |
- 복제 전용 환경에서는
MINIMAL로 스토리지와 네트워크를 절약할 수 있음. - CDC 도구 연동 시에는 타겟 시스템이 Row의 전체 상태를 필요로 하므로
FULL이 필수임.
-- 현재 설정 확인
SHOW VARIABLES LIKE 'binlog_row_image';
-- 동적 변경
SET GLOBAL binlog_row_image = 'MINIMAL';