binlog 포맷(STATEMENT/ROW/MIXED)
같은 변경, 세 가지 기록 방식
binlog에 변경을 "어떻게" 저장하느냐에 따라 복제 안정성, 파일 크기, CDC 도구 호환성이 크게 달라진다. MySQL은 세 가지 포맷을 binlog_format 변수로 제어해왔다. MySQL 8.4에서 이 변수는 deprecated 됐고, ROW 포맷이 사실상 표준으로 자리잡는 중이다.
STATEMENT 포맷
실행된 SQL 문 자체를 binlog에 기록한다. UPDATE orders SET status='done' WHERE region='KR' 처럼 원본 쿼리가 그대로 남기 때문에 로그 파일이 작다.
한계: UUID(), NOW(), RAND() 같은 비결정적 함수는 소스와 레플리카에서 결과가 달라질 수 있다. 트리거나 저장 프로시저 내부의 변경이 제대로 복제되지 않을 위험도 있다.
ROW 포맷
SQL 대신 행(row) 단위의 실제 변경 데이터를 기록한다. 이벤트 흐름은 다음과 같다.
Table_map이벤트 — 테이블 구조와 임시 ID 매핑Write_rows/Update_rows/Delete_rows— 실제 변경된 행 데이터
비결정적 함수 문제가 없어 복제가 안전하다. MySQL 8.0부터 기본값이다. 단점은 대량 UPDATE 한 줄이 수십만 개의 행 이벤트로 기록될 수 있어 파일 크기가 커질 수 있다는 점이다.
# ROW 포맷 이벤트를 사람이 읽기 쉽게 출력
mysqlbinlog --verbose binlog.000003
# 출력 예: ### @1=42 @2='pending' @3=...MIXED 포맷
STATEMENT를 기본으로 쓰다가, 비결정적이라고 판단되는 SQL은 자동으로 ROW로 전환한다. 절충안이지만, 어떤 이벤트가 어떤 방식으로 기록됐는지 추적이 어려워 CDC 도구와의 궁합이 좋지 않다.
세 포맷 비교
MySQL 8.0.34 이후: ROW로 수렴
binlog_format 변수는 MySQL 8.0.34에서 deprecated 됐고, 향후 제거될 예정이다. ROW 포맷이 유일한 포맷이 된다.
-- 현재 포맷 확인
SELECT @@binlog_format;
-- 명시적으로 ROW 설정
SET PERSIST binlog_format = 'ROW';ROW 포맷 세부 조정: binlog_row_image
ROW 포맷을 사용할 때 얼마나 많은 컬럼을 기록할지 binlog_row_image로 제어한다.
| 값 | 설명 |
|---|---|
FULL (기본) | 변경 전·후 모든 컬럼 기록 |
MINIMAL | 변경 전: 행 식별에 필요한 컬럼만 / 변경 후: 실제 바뀐 컬럼만 |
NOBLOB | BLOB/TEXT 제외, 나머지 전체 기록 |
스토리지 절감이 필요하다면 MINIMAL을 쓸 수 있지만, 모든 레플리카와 CDC 도구가 지원하는지 먼저 확인해야 한다.
MySQL 8.0에서 추가된 binlog_row_value_options = PARTIAL_JSON을 설정하면 JSON 컬럼의 일부만 변경됐을 때 전체 JSON 대신 변경된 경로만 기록해 binlog 크기를 줄일 수 있다.
References
- https://dev.mysql.com/doc/refman/8.4/en/binary-log-formats.html
- https://dev.mysql.com/doc/refman/8.4/en/replication-sbr-rbr.html
- https://dev.mysql.com/doc/refman/8.4/en/binary-log-setting.html
- https://mariadb.com/docs/server/server-management/server-monitoring-logs/binary-log/binary-log-formats
- https://aws.amazon.com/blogs/database/best-practices-for-configuring-parameters-for-amazon-rds-for-mysql-part-2-parameters-related-to-replication/