카테고리 : MySQL/기술노트

binlog_row_image 설계: FULL과 MINIMAL의 장단점

MySQL row event의 before/after image 구성과 FULL·MINIMAL 선택이 복제, CDC, 로그 용량, 장애 대응에 미치는 영향을 정리한다.

저자: MySQL 기술 노트 작성: 2026.09.03 약 15분 8,797자
다운로드

binlog_row_image 설계: FULL과 MINIMAL의 장단점

binlog_row_image는 row-based binary logging에서 각 변경 행의 어느 열을 binary log에 기록할지 결정한다. FULL은 행의 전체 상태를 넓게 보존하고, MINIMAL은 행 식별과 변경 적용에 필요한 열만 기록한다. 전자는 CDC 호환성과 사후 분석이 단순하지만 로그가 커질 수 있고, 후자는 저장 공간과 전송량을 줄일 수 있지만 소비자가 완전한 행을 기대하거나 source와 replica의 스키마가 다르면 위험해질 수 있다.

따라서 이 변수는 단순한 용량 최적화 스위치가 아니다. source, replica, CDC, 감사·복구 도구 사이에서 변경 데이터를 어떤 형태로 전달할지 정하는 인터페이스 계약이다. 값 변경은 binary log 크기뿐 아니라 이벤트 해석 가능성, replica의 행 탐색, downstream 데이터 모델, 장애 조사 범위까지 함께 검토해야 한다.

1. Row event와 행 이미지의 구조

binlog_format=ROW에서 DML은 원래 SQL 문자열보다 실제로 변경된 행을 중심으로 기록된다. 행 이미지의 역할은 DML 종류마다 다르다.

  • INSERT: 기존 행이 없으므로 after image만 필요하다.
  • DELETE: 삭제 뒤의 행이 없으므로 before image만 필요하다.
  • UPDATE: 기존 행을 찾는 before image와 적용할 값을 담는 after image가 모두 필요하다.
flowchart LR
    A[애플리케이션 DML] --> B{DML 종류}
    B -->|INSERT| C[after image]
    B -->|DELETE| D[before image]
    B -->|UPDATE| E[before image]
    B -->|UPDATE| F[after image]
    C --> G[Binary log row event]
    D --> G
    E --> G
    F --> G
    G --> H[Replica applier]
    G --> I[CDC consumer]
    G --> J[mysqlbinlog 분석]

before image는 replica가 갱신·삭제할 기존 행을 식별하는 자료다. after image는 삽입할 값 또는 갱신할 값을 전달한다. binlog_row_image는 이 두 이미지의 열 구성을 조절하지만, 트랜잭션 경계나 row event 자체를 없애지는 않는다.

이 설정은 row event에만 영향을 준다. binlog_format=STATEMENT로 기록되는 이벤트에는 효력이 없고, MIXED에서는 실제로 row 형식으로 기록된 변경에만 적용된다. DDL 또한 ROW 설정만 보고 행 이미지가 된다고 가정해서는 안 된다.

2. FULL, MINIMAL, NOBLOB의 차이

MySQL은 세 가지 값을 제공한다.

before image after image 주된 성격
FULL 모든 열 모든 열 호환성과 관측성 우선
MINIMAL 행 식별에 필요한 최소 열 SQL이 값을 지정했거나 자동 생성된 열 중심 로그 크기 절감 우선
NOBLOB FULL에 가깝지만 불필요한 BLOB·TEXT 제외 변경되지 않은 BLOB·TEXT 제외 큰 LOB 열의 비용을 줄이는 절충안

기본값은 FULL이다. MINIMAL의 “최소”는 단순히 primary key 한 열만 의미하지 않는다. MySQL은 다음 순서로 before image의 행 식별 열을 결정한다.

  1. primary key가 있으면 primary key 열을 기록한다.
  2. primary key가 없고 모든 구성 열이 NOT NULL인 unique key가 있으면 해당 키 열을 사용할 수 있다.
  3. 둘 다 없으면 행 식별을 위해 모든 열을 기록해야 한다.

따라서 primary key가 없는 테이블에서는 MINIMAL을 설정해도 기대만큼 로그가 줄지 않을 수 있다. 더 중요한 문제는 replica가 효율적으로 대상 행을 찾지 못해 적용 비용이 커질 수 있다는 점이다. row image 최적화보다 안정적인 primary key 설계가 먼저다.

3. UPDATE 한 건으로 보는 실제 차이

다음과 같은 테이블과 변경을 가정한다.

CREATE TABLE customer_profile (
    customer_id BIGINT PRIMARY KEY,
    email       VARCHAR(255) NOT NULL,
    status      VARCHAR(20)  NOT NULL,
    memo        TEXT,
    updated_at  DATETIME(6)  NOT NULL
);

UPDATE customer_profile
SET status = 'ACTIVE',
    updated_at = CURRENT_TIMESTAMP(6)
WHERE customer_id = 1001;

FULL에서는 개념적으로 before image와 after image에 customer_id, email, status, memo, updated_at 전체가 포함된다. 반면 MINIMAL에서는 before image에 customer_id, after image에 이번 문장에서 지정된 statusupdated_at을 중심으로 기록한다.

mysqlbinlog --base64-output=DECODE-ROWS -vv로 보면 핵심 형태는 다음과 유사하다. 아래 출력은 열 구성을 설명하기 위해 단순화한 예시이며, 실제 파일명·event 위치·메타데이터 표시는 환경에 따라 달라진다.

# FULL의 개념적 형태
### UPDATE `app`.`customer_profile`
### WHERE
###   @1=1001
###   @2='old@example.com'
###   @3='PENDING'
###   @4='...'
###   @5='2026-09-03 08:59:00.000000'
### SET
###   @1=1001
###   @2='old@example.com'
###   @3='ACTIVE'
###   @4='...'
###   @5='2026-09-03 09:00:00.000000'

# MINIMAL의 개념적 형태
### UPDATE `app`.`customer_profile`
### WHERE
###   @1=1001
### SET
###   @3='ACTIVE'
###   @5='2026-09-03 09:00:00.000000'

이 차이는 행이 넓고 실제로 바뀌는 열이 적을수록 커진다. 특히 큰 TEXT, BLOB, JSON 열이 있는 테이블에서 작은 상태값만 자주 갱신하면 FULL의 before/after image 비용이 두드러질 수 있다. 그러나 절감 효과는 추정만으로 결정하지 말고 대표 트랜잭션의 실제 row event 크기와 일일 binary log 생성량으로 측정해야 한다.

4. 현재 설정과 적용 범위 확인

변경 전에는 binlog_format, global/session row image, binary logging 활성화 여부를 함께 확인한다.

SELECT VERSION() AS mysql_version,
       @@GLOBAL.binlog_format AS global_binlog_format,
       @@SESSION.binlog_format AS session_binlog_format,
       @@GLOBAL.binlog_row_image AS global_row_image,
       @@SESSION.binlog_row_image AS session_row_image,
       @@GLOBAL.binlog_row_metadata AS row_metadata,
       @@GLOBAL.log_bin AS binary_logging_enabled;

실행 결과(MySQL 8.0.x):

mysql> SELECT VERSION() AS mysql_version,
    ->        @@GLOBAL.binlog_format AS global_binlog_format,
    ->        @@SESSION.binlog_format AS session_binlog_format,
    ->        @@GLOBAL.binlog_row_image AS global_row_image,
    ->        @@SESSION.binlog_row_image AS session_row_image,
    ->        @@GLOBAL.binlog_row_metadata AS row_metadata,
    ->        @@GLOBAL.log_bin AS binary_logging_enabled;

+---------------+----------------------+-----------------------+------------------+-------------------+--------------+------------------------+
| mysql_version | global_binlog_format | session_binlog_format | global_row_image | session_row_image | row_metadata | binary_logging_enabled |
+---------------+----------------------+-----------------------+------------------+-------------------+--------------+------------------------+
| 8.0.46        | ROW                  | ROW                   | FULL             | FULL              | MINIMAL      |                      0 |
+---------------+----------------------+-----------------------+------------------+-------------------+--------------+------------------------+
1 row in set (0.00 sec)

log_bin=OFF라면 변수 값은 조회되더라도 실제 binary log 파일은 생성되지 않는다. 또한 global 값은 새 연결의 기본값이고, 이미 열린 세션은 다른 session 값을 유지할 수 있다. connection pool을 사용하는 환경에서는 global 값 한 번의 조회만으로 모든 트랜잭션의 실제 기록 방식을 단정해서는 안 된다.

binlog_row_image는 global 및 session 범위에서 동적으로 변경할 수 있다. 다음 예제는 현재 연결에만 적용되는 동작을 확인한 뒤 원래 기본 방향인 FULL로 되돌린다.

SET SESSION binlog_row_image = 'MINIMAL';
SELECT @@SESSION.binlog_row_image AS session_row_image_after_change;

SET SESSION binlog_row_image = 'FULL';
SELECT @@SESSION.binlog_row_image AS session_row_image_after_restore;

실행 결과(MySQL 8.0.x):

mysql> SET SESSION binlog_row_image = 'MINIMAL';

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@SESSION.binlog_row_image AS session_row_image_after_change;

+--------------------------------+
| session_row_image_after_change |
+--------------------------------+
| MINIMAL                        |
+--------------------------------+
1 row in set (0.00 sec)

mysql> SET SESSION binlog_row_image = 'FULL';

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@SESSION.binlog_row_image AS session_row_image_after_restore;

+---------------------------------+
| session_row_image_after_restore |
+---------------------------------+
| FULL                            |
+---------------------------------+
1 row in set (0.00 sec)

동적 변경이 가능하다는 사실과 운영 중 임의 변경이 안전하다는 판단은 다르다. session별 값이 혼재하면 같은 테이블의 이벤트도 연결에 따라 다른 image 정책으로 기록될 수 있다. 변경은 parameter/configuration을 source of truth로 관리하고, 기존 connection pool 교체와 재접속 검증까지 포함해야 한다.

5. FULL을 선택할 때 얻는 것

5.1 CDC 소비자와의 계약이 단순하다

FULL은 update 전후의 전체 행을 제공하므로 CDC가 완전한 before/after 레코드를 구성하기 쉽다. 필터링, 라우팅, 감사, downstream의 파생 계산이 변경되지 않은 열을 참조하더라도 source row event 자체에 값이 들어 있다.

반대로 MINIMAL에서는 변경되지 않은 열이 after image에 없을 수 있다. CDC 도구가 이전 상태 저장소나 schema history를 이용해 행을 보강할 수 있는지, 누락 열을 NULL과 구분하는지, delete의 key를 어떻게 구성하는지 확인해야 한다. 제품이 MINIMAL을 읽을 수 있다는 것과 현재 파이프라인의 모든 변환이 올바르게 동작한다는 것은 별개다.

AWS DMS처럼 source 설정으로 binlog_row_image=FULL을 요구하는 서비스가 있다. 이런 소비자가 하나라도 연결되어 있다면 로그 절감만을 이유로 MINIMAL을 선택해서는 안 된다.

5.2 사후 분석과 감사 자료가 풍부하다

FULL row event는 변경 전후의 전체 열을 더 많이 남기므로 mysqlbinlog -vv를 통한 장애 조사와 데이터 변경 분석에 유리하다. 어떤 값이 바뀌지 않았는지까지 사건 시점의 이미지에서 확인할 수 있다.

다만 binary log를 완전한 감사 시스템으로 간주해서는 안 된다. 보존 기간, 접근 권한, DDL/schema history, event 해석 도구, 암호화와 무결성 관리가 함께 있어야 한다. FULL이라는 설정 하나가 사용자 식별과 업무 맥락까지 기록해 주지는 않는다.

5.3 이기종·비대칭 스키마 위험을 줄이는 방향이다

전통적인 source-replica 구조에서도 DDL 배포 시점 차이, 열 순서·타입 차이, key 정의 불일치는 위험하다. 특히 MINIMAL 또는 NOBLOB을 사용할 때 source와 destination은 열의 존재·순서·데이터 타입, primary key 정의가 일치해야 안전한 적용을 보장하기 쉽다.

FULL도 임의의 스키마 차이를 정당화하지는 않는다. 다만 더 풍부한 row image는 일부 도구의 검증과 문제 분석에 유리하다. 복제 토폴로지에서 의도적으로 서로 다른 스키마를 운영한다면 실제 버전과 변환 규칙을 별도로 검증해야 한다.

6. FULL의 비용

FULL의 비용은 행 수뿐 아니라 행 폭과 변경 패턴에 따라 결정된다.

  • 넓은 행을 소수 열만 갱신해도 전체 before/after image가 기록된다.
  • 큰 트랜잭션은 source의 binlog cache와 임시 파일 사용을 늘릴 수 있다.
  • binary log 파일 생성 속도와 보존 공간 요구가 커진다.
  • replica 전송량과 relay log 쓰기량이 증가한다.
  • CDC connector와 downstream queue가 처리해야 할 payload가 커진다.
  • TEXT, BLOB, JSON 값이 반복 포함되면 효과가 특히 커질 수 있다.

여기서 “로그가 두 배가 된다”처럼 고정 배수를 가정하면 안 된다. event header, table map, 트랜잭션 크기, 압축, 실제 변경 열, key 구성, 데이터 타입에 따라 비율이 달라진다. 최소한 다음 지표를 변경 전후 동일한 업무 구간에서 비교해야 한다.

  • 시간당·일일 binary log bytes
  • 트랜잭션당 평균 및 상위 백분위 event 크기
  • Binlog_cache_useBinlog_cache_disk_use 증가량
  • replica network throughput과 relay log 증가량
  • replica apply lag와 CDC source lag
  • 보존 정책을 포함한 최대 필요 디스크 공간

7. MINIMAL을 선택할 때 얻는 것과 잃는 것

7.1 기대할 수 있는 이점

MINIMAL은 안정적인 primary key가 있고, 행이 넓으며, update가 일부 열에 집중되는 환경에서 효과가 크다. binary log 쓰기, 네트워크 전송, relay log, CDC 입력 payload를 줄일 가능성이 있다. 큰 변경량 때문에 replica나 CDC가 I/O 대역폭에 묶이는 환경에서는 실질적인 개선점이 될 수 있다.

7.2 감수해야 할 제약

  • CDC event의 after image가 완전한 최신 행이 아닐 수 있다.
  • 변경되지 않은 열을 필요로 하는 필터·감사·파생 로직이 깨질 수 있다.
  • primary key가 없는 테이블에서는 before image가 여전히 넓고 replica 적용도 비효율적일 수 있다.
  • source와 destination의 열 순서·타입·primary key 정의 차이가 더 위험해질 수 있다.
  • 이미 MINIMAL로 기록된 과거 event에 없는 열은 나중에 FULL로 되돌려도 복원되지 않는다.
  • 일부 관리형 CDC·zero-ETL·검색 수집 서비스는 명시적으로 FULL을 요구한다.

결론적으로 MINIMAL은 “안전한 기본값의 경량 버전”이 아니라 소비자와 스키마 조건을 증명했을 때 선택하는 최적화다.

8. Primary Key 준비 상태 점검

다음 재현 예제는 primary key, UNIQUE NOT NULL, nullable unique key만 가진 세 테이블을 만들고, MINIMAL before image의 행 식별 기반을 분류한다. 운영 전체 스키마를 점검할 때는 TABLE_SCHEMA 필터를 실제 데이터베이스로 바꾸고 시스템 스키마를 제외한다.

DROP TABLE IF EXISTS row_image_demo_pk;
DROP TABLE IF EXISTS row_image_demo_unique;
DROP TABLE IF EXISTS row_image_demo_nullable;

CREATE TABLE row_image_demo_pk (
    id BIGINT NOT NULL PRIMARY KEY,
    payload VARCHAR(100) NOT NULL
) ENGINE=InnoDB;

CREATE TABLE row_image_demo_unique (
    id BIGINT NOT NULL,
    business_key VARCHAR(40) NOT NULL,
    payload VARCHAR(100) NOT NULL,
    UNIQUE KEY uk_business_key (business_key)
) ENGINE=InnoDB;

CREATE TABLE row_image_demo_nullable (
    id BIGINT NOT NULL,
    optional_key VARCHAR(40) NULL,
    payload VARCHAR(100) NOT NULL,
    UNIQUE KEY uk_optional_key (optional_key)
) ENGINE=InnoDB;

WITH index_quality AS (
    SELECT s.TABLE_SCHEMA,
           s.TABLE_NAME,
           s.INDEX_NAME,
           s.NON_UNIQUE,
           MIN(c.IS_NULLABLE = 'NO') AS all_columns_not_null
    FROM information_schema.STATISTICS AS s
    JOIN information_schema.COLUMNS AS c
      ON c.TABLE_SCHEMA = s.TABLE_SCHEMA
     AND c.TABLE_NAME = s.TABLE_NAME
     AND c.COLUMN_NAME = s.COLUMN_NAME
    WHERE s.TABLE_SCHEMA = DATABASE()
      AND s.TABLE_NAME LIKE 'row_image_demo_%'
    GROUP BY s.TABLE_SCHEMA, s.TABLE_NAME, s.INDEX_NAME, s.NON_UNIQUE
), table_quality AS (
    SELECT TABLE_SCHEMA,
           TABLE_NAME,
           MAX(INDEX_NAME = 'PRIMARY') AS has_primary_key,
           MAX(NON_UNIQUE = 0 AND all_columns_not_null = 1) AS has_nonnull_unique_key
    FROM index_quality
    GROUP BY TABLE_SCHEMA, TABLE_NAME
)
SELECT t.TABLE_NAME,
       CASE
           WHEN COALESCE(q.has_primary_key, 0) = 1 THEN 'PRIMARY KEY'
           WHEN COALESCE(q.has_nonnull_unique_key, 0) = 1 THEN 'UNIQUE NOT NULL'
           ELSE 'ALL COLUMNS'
       END AS minimal_before_image_basis
FROM information_schema.TABLES AS t
LEFT JOIN table_quality AS q
  ON q.TABLE_SCHEMA = t.TABLE_SCHEMA
 AND q.TABLE_NAME = t.TABLE_NAME
WHERE t.TABLE_SCHEMA = DATABASE()
  AND t.TABLE_NAME LIKE 'row_image_demo_%'
ORDER BY t.TABLE_NAME;

DROP TABLE row_image_demo_pk;
DROP TABLE row_image_demo_unique;
DROP TABLE row_image_demo_nullable;

실행 결과(MySQL 8.0.x):

아래는 준비 DDL과 핵심 조회 결과를 발췌한 것이다. 반복되는 정리 DDL의 성공 메시지는 생략했다.

mysql> CREATE TABLE row_image_demo_pk (...);
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE row_image_demo_unique (...);
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE row_image_demo_nullable (...);
Query OK, 0 rows affected (0.01 sec)

mysql> WITH index_quality AS (...), table_quality AS (...)
    -> SELECT t.TABLE_NAME,
    ->        CASE ... END AS minimal_before_image_basis
    -> FROM information_schema.TABLES AS t
    -> LEFT JOIN table_quality AS q ...
    -> ORDER BY t.TABLE_NAME;

+-------------------------+----------------------------+
| TABLE_NAME              | minimal_before_image_basis |
+-------------------------+----------------------------+
| row_image_demo_nullable | ALL COLUMNS                |
| row_image_demo_pk       | PRIMARY KEY                |
| row_image_demo_unique   | UNIQUE NOT NULL            |
+-------------------------+----------------------------+
3 rows in set (0.01 sec)

nullable unique key는 여러 NULL을 허용할 수 있으므로 안정적인 단일 행 식별 기준으로 취급할 수 없다. 실제 운영 점검에서는 단순히 unique index 개수만 세지 말고 모든 key column의 null 허용 여부와 source/replica 정의의 일치 여부를 확인해야 한다.

9. NOBLOB과 부분 JSON 업데이트를 혼동하지 않는다

NOBLOB은 전체 열 중심의 관측성을 유지하면서 필요하지 않은 BLOB·TEXT 계열을 제외하는 절충안이다. 그러나 다음 한계가 있다.

  • 큰 열이 JSON, 긴 VARCHAR, binary 계열 등 다른 타입이면 기대한 절감이 없을 수 있다.
  • 변경된 BLOB·TEXT 값이나 행 식별에 필요한 값은 여전히 기록된다.
  • CDC가 변경 전 전체 LOB 값을 요구하면 기능 요구를 충족하지 못할 수 있다.

binlog_row_value_options=PARTIAL_JSON도 별도의 기능이다. 이는 JSON의 partial update 표현과 관련되고, row image에서 어느 열을 포함할지 정하는 binlog_row_image를 대체하지 않는다. binlog_row_metadata 역시 테이블 메타데이터의 양을 조절하는 변수이지 행 값의 before/after 범위를 결정하는 변수가 아니다.

세 변수를 독립된 축으로 구분해야 한다.

변수 결정 대상
binlog_row_image before/after image에 포함할 열 값
binlog_row_metadata row event에 포함할 테이블 메타데이터 양
binlog_row_value_options 지원되는 값 타입의 부분 변경 표현 방식

10. 실제 binary log 검증 절차

운영 변경 전 staging에서 대표 INSERT, UPDATE, DELETE를 실행하고 row event를 직접 비교한다. 파일 경로와 시간 범위는 대상 환경에 맞게 조정한다.

mysqlbinlog \
  --start-datetime="2026-09-03 08:50:00" \
  --stop-datetime="2026-09-03 09:10:00" \
  --base64-output=DECODE-ROWS \
  --verbose --verbose \
  /var/lib/mysql/binlog.000123

검증 시 다음을 확인한다.

  1. 대상 DML이 실제 row event로 기록되었는가.
  2. UPDATE_ROWS_EVENT의 before image가 어떤 key column을 포함하는가.
  3. after image에 변경된 열만 있는지, 전체 열이 있는지 확인했는가.
  4. 큰 LOB/JSON 열이 event 크기에 미치는 영향을 측정했는가.
  5. CDC가 update와 delete event를 기대한 schema로 변환하는가.
  6. replica가 keyless table에서 지연되지 않는가.
  7. DDL 이후 source와 replica의 열 순서·타입·key 정의가 일치하는가.

Binary log에는 업무 데이터가 포함될 수 있다. 검증 파일을 외부로 복사할 때는 개인정보와 비밀값 노출 여부를 확인하고, 운영 파일 접근 권한과 보존·암호화 정책을 지켜야 한다.

11. Aurora MySQL에서의 운영 해석

Aurora cluster 내부의 storage replication과 MySQL binary log 기반 논리 복제는 서로 다른 계층이다. Aurora의 고가용성이 storage 계층에서 제공되더라도 외부 CDC, DMS, 다른 MySQL로의 binlog replication에는 row image 계약이 그대로 중요하다.

Aurora에서는 다음 사항을 추가로 확인한다.

  • binlog_row_image를 DB cluster parameter group에서 어떻게 관리하는지 확인한다.
  • parameter 적용 상태와 writer 교체·failover 뒤 값을 재검증한다.
  • AWS DMS source는 일반적으로 FULL을 요구하므로 임의로 MINIMAL로 낮추지 않는다.
  • Aurora zero-ETL 또는 OpenSearch Ingestion 같은 기능은 엔진 버전과 구성에 따라 binlog_row_image=FULL, binlog_row_metadata=FULL 등 명시적 선행 조건을 요구할 수 있다.
  • enhanced binlog 사용 여부와 별개로 downstream이 요구하는 row image를 확인한다.
  • binary logging 활성화에 따른 writer 부하와 보존 비용을 실제 change rate로 측정한다.

관리형 서비스의 요구 조건은 변경될 수 있으므로, parameter 변경 시점의 대상 서비스 문서를 기준으로 확인해야 한다. MySQL 자체에서 MINIMAL이 유효하다는 사실만으로 Aurora 연동 서비스가 이를 지원한다고 추론해서는 안 된다.

12. 대표적인 실패 모드

실패 모드 1: 로그는 줄었지만 CDC 필드가 사라진다

원인은 MINIMAL after image에 변경되지 않은 열이 없는데 downstream이 완전한 행으로 해석한 경우가 많다. connector 지원 여부, event envelope, stateful reconstruction, delete key 처리, schema history를 확인한다. 소비자가 FULL을 요구한다면 source 설정을 되돌리고 누락 기간의 재처리 범위를 결정해야 한다.

실패 모드 2: MINIMAL로 바꿨는데 용량이 거의 줄지 않는다

primary key나 non-null unique key가 없는 테이블, 전체 열을 갱신하는 SQL, 변경되는 LOB 값, 소수의 좁은 테이블이 원인일 수 있다. 테이블별 change volume과 row width를 분리해 측정한다. 설정값만 보고 절감 효과를 가정하지 않는다.

실패 모드 3: replica 적용이 느리거나 불일치한다

keyless table, source/replica의 key 정의 불일치, 열 순서·타입 차이, 장기 DDL 불일치 구간을 확인한다. MINIMAL 또는 NOBLOB 환경에서는 스키마가 동일하다는 전제가 특히 중요하다. 설정을 FULL로 되돌리는 조치만으로 이미 발생한 데이터 불일치는 복구되지 않으므로 checksum과 재동기화가 필요하다.

실패 모드 4: global 값과 실제 event가 다르다

기존 세션, connection pool 초기화 SQL, session override, MIXED에서 statement로 기록된 문장을 확인한다. global 변수는 모든 기존 연결의 현재 값을 대표하지 않는다.

실패 모드 5: FULL 전환 후 보존 공간이 급격히 줄어든다

대량 update/delete, 넓은 행, 긴 보존 기간, CDC 지연, replica 장애가 함께 작용할 수 있다. binary log 생성률과 purge 가능 시점을 기준으로 용량을 다시 산정하고, 큰 트랜잭션 분할과 downstream 처리량 확장을 검토한다. 저장 공간 문제를 숨기기 위해 검증 없이 MINIMAL로 되돌리는 것은 적절하지 않다.

13. 선택 기준

다음 조건이면 FULL이 안전한 기본 선택이다.

  • CDC 또는 관리형 연동 서비스가 FULL을 요구한다.
  • update 전후의 완전한 행이 감사·분석에 필요하다.
  • 여러 downstream 소비자의 요구를 단순한 공통 계약으로 유지해야 한다.
  • source와 replica의 스키마 차이 가능성을 완전히 제거하지 못했다.
  • 로그 크기보다 복제 정확성과 조사 가능성이 우선한다.

다음 조건을 모두 검증했다면 MINIMAL을 고려할 수 있다.

  • 모든 고변경 테이블에 안정적인 primary key 또는 적합한 non-null unique key가 있다.
  • source와 replica의 열 순서·타입·primary key 정의가 일치한다.
  • 모든 CDC 소비자가 sparse before/after image를 올바르게 처리한다.
  • 완전한 before/after 행을 요구하는 감사·복구 절차가 없다.
  • staging의 실제 row event와 downstream 결과를 확인했다.
  • 측정 결과 binary log·네트워크·relay log 절감 효과가 유의미하다.
  • 즉시 FULL로 되돌릴 수 있는 변경·모니터링 계획이 있다.

BLOB·TEXT가 주된 비용이고 소비자 요구가 허용한다면 NOBLOB을 별도 후보로 검증할 수 있다. 이름만 보고 FULL과 동일하거나 MINIMAL보다 안전하다고 단정하지 말고 실제 event를 확인해야 한다.

14. 변경 전후 체크리스트

변경 전

  • binlog_format=ROW 또는 MIXED
  • 대표 트랜잭션의 실제 row event를 mysqlbinlog -vv

변경 후

  • 신규 세션의 @@SESSION.binlog_row_image
  • 실제 WRITE_ROWS, UPDATE_ROWS, DELETE_ROWS
  • Binlog_cache_disk_use

15. 결론

binlog_row_image의 핵심은 로그 크기와 변경 정보의 완전성 사이의 선택이다. FULL은 더 많은 데이터를 기록하는 대신 CDC 호환성, 사후 분석, 운영 계약을 단순하게 만든다. MINIMAL은 key와 변경 열 중심으로 event를 줄일 수 있지만, primary key 품질, 동일한 복제 스키마, sparse image를 이해하는 소비자가 전제되어야 한다. NOBLOB은 특정 LOB 비용을 줄이는 절충안이지만 별도 검증이 필요하다.

신규 환경의 출발점은 대체로 FULL이 안전하다. MINIMAL은 binary log가 실제 병목이고, 모든 소비자와 스키마 조건을 검증했으며, 측정 가능한 절감 효과가 있을 때 적용하는 최적화로 다루는 편이 타당하다. 다음 단계에서는 binlog_row_metadata와 schema history가 CDC의 DDL 해석과 열 타입 복원에 어떤 영향을 주는지 함께 살펴볼 수 있다.