MySQL Binary Log 개요: 복제, 시점 복구, 감사 추적의 기반
MySQL Binary Log의 이벤트 구조와 커밋 경로를 살펴보고 복제, 시점 복구, 변경 추적에서의 역할과 운영 기준을 정리한다.
MySQL Binary Log 개요: 복제, 시점 복구, 감사 추적의 기반
Binary Log는 MySQL 서버에서 일어난 논리적 변경을 시간 순서대로 기록하는 이벤트 스트림이다. 복제 소스는 이 스트림을 replica에 전달하고, 복구 담당자는 전체 백업 이후의 이벤트를 다시 실행해 장애 직전 시점까지 데이터를 전진시킨다. 보안·감사 담당자는 같은 기록에서 변경 주체와 영향을 받은 객체를 추적할 단서를 얻을 수 있다.
그러나 Binary Log를 단순한 “SQL 실행 이력 파일”로 이해하면 운영 판단이 어긋난다. ROW 형식에서는 원문 SQL 대신 변경 전후 row image가 핵심이며, 일반 조회는 기록되지 않는다. 보존 기간이 지나 파일이 삭제되면 복제와 시점 복구의 공통 기반도 사라진다. 이 장에서는 Binary Log의 내부 위치와 이벤트 구조를 먼저 이해하고, 복제·PITR(Point-in-Time Recovery)·감사 추적이라는 세 용도에 맞추어 설계와 점검 기준을 정리한다.
1. Binary Log는 스토리지 엔진 로그가 아니다
MySQL의 지속성 경로에는 목적이 다른 로그가 함께 등장한다.
- InnoDB Redo Log: 비정상 종료 후 이미 커밋된 페이지 변경을 복구하는 물리적 성격의 로그다.
- InnoDB Undo Log: 트랜잭션 롤백과 MVCC의 이전 버전 읽기를 지원한다.
- Binary Log: 스토리지 엔진 위의 MySQL 서버 계층에서 논리적 변경 이벤트를 기록한다.
- Relay Log: replica가 소스의 Binary Log 이벤트를 받아 로컬에 저장한 복제용 중간 로그다.
- General Log·Audit Log: 접속과 문장 실행을 관찰하는 목적의 로그다. Binary Log와 수집 범위가 다르다.
Binary Log는 InnoDB에만 종속되지 않는다. 서버 계층이 기록하므로 Binary Log를 지원하는 다른 스토리지 엔진의 변경도 포함할 수 있다. 반대로 Redo Log만 보존해서는 다른 서버에 논리 변경을 재생하거나 특정 업무 시점으로 복구할 수 없다.
flowchart LR
A[클라이언트 트랜잭션] --> B[MySQL 서버 계층]
B --> C[InnoDB 변경]
C --> D[Redo와 Undo]
B --> E[Binary Log 이벤트]
E --> F[복제 전송]
E --> G[전체 백업 이후 PITR]
E --> H[변경 추적 단서]
F --> I[Replica Relay Log]
I --> J[Replica 적용]
핵심은 Redo Log와 Binary Log가 서로 대체 관계가 아니라는 점이다. Redo Log는 해당 InnoDB 인스턴스의 충돌 복구에 최적화되어 있고, Binary Log는 서버 간 전달과 논리적 재생에 최적화되어 있다.
2. 파일과 이벤트의 기본 구조
Binary Log는 일반적으로 번호가 붙은 여러 파일과 index 파일로 관리된다. 파일 내부에는 단순 텍스트 SQL 목록이 아니라 헤더와 다양한 이벤트가 이진 형식으로 저장된다. 대표적인 이벤트는 다음과 같다.
| 이벤트 범주 | 역할 |
|---|---|
Format_description |
파일에서 사용할 이벤트 형식과 버전 정보를 제공한다. |
Previous_gtids·Gtid |
GTID 실행 이력과 현재 트랜잭션 식별자를 전달한다. |
Query |
DDL 또는 STATEMENT 기반 문장을 전달한다. |
Table_map |
뒤따르는 row event가 어느 테이블을 대상으로 하는지 연결한다. |
Write_rows·Update_rows·Delete_rows |
ROW 형식에서 row 변경을 표현한다. |
Xid |
트랜잭션 커밋 경계를 나타낸다. |
Rotate |
다음 Binary Log 파일로 전환되었음을 나타낸다. |
이벤트에는 position과 end_log_pos가 있어 파일 안에서 재생 범위를 지정할 수 있다. GTID를 사용하면 파일·position보다 트랜잭션 단위 식별과 중복 방지가 쉬워진다. 다만 GTID가 Binary Log 파일 자체를 없애는 것은 아니다. GTID도 Binary Log 이벤트에 실려 전달되며, 보존과 정리 정책은 여전히 필요하다.
2.1 기록 형식: ROW, STATEMENT, MIXED
운영 환경에서는 보통 ROW를 기준으로 이해하는 편이 안전하다.
ROW: 실제로 변경된 row image를 기록한다. 비결정적 함수나 실행 계획 차이로 인한 재현 오류를 줄인다.STATEMENT: 변경을 일으킨 SQL 문장을 기록한다. 데이터 분포와 실행 환경이 다르면 결과가 달라질 수 있는 문장을 주의해야 한다.MIXED: 서버가 문장 특성에 따라 statement 또는 row 이벤트를 선택한다.
ROW는 “모든 row의 완전한 이전·이후 값이 항상 들어 있다”는 뜻이 아니다. binlog_row_image가 FULL, MINIMAL, NOBLOB 중 무엇인지에 따라 기록되는 열 범위가 달라진다. 복제에는 MINIMAL이 효율적일 수 있지만, Binary Log만으로 변경 전후 값을 조사하려는 요구가 강하면 정보량의 차이를 먼저 평가해야 한다.
3. 트랜잭션 커밋과 Binary Log
InnoDB 트랜잭션에서는 스토리지 엔진의 커밋 상태와 Binary Log 기록이 서로 어긋나지 않아야 한다. 한쪽만 커밋되면 소스에는 데이터가 있지만 replica와 PITR 스트림에는 없거나, 반대로 재생할 이벤트는 있는데 소스 트랜잭션은 롤백된 모순이 생긴다.
MySQL은 이 일관성을 위해 내부적으로 2단계 커밋 협력을 사용한다. 개념적으로는 다음 순서를 거친다.
- InnoDB가 트랜잭션을 prepare 상태로 만든다.
- 서버가 트랜잭션 이벤트를 Binary Log cache에서 파일로 넘긴다.
- 내구성 설정에 따라 Binary Log를 디스크에 동기화한다.
- InnoDB가 트랜잭션 커밋을 완료하고 Redo Log 내구성 규칙을 적용한다.
- 장애 복구 시 prepare 상태와 Binary Log 기록을 대조해 일관된 결론을 낸다.
실제 서버는 처리량을 높이기 위해 여러 트랜잭션의 flush와 sync를 묶는 group commit을 사용한다. 따라서 “트랜잭션마다 파일 동기화가 완전히 직렬로 한 번씩 일어난다”고 해석해서는 안 된다. sync_binlog=1과 innodb_flush_log_at_trx_commit=1은 일반적으로 가장 강한 커밋 내구성 기준이지만, 스토리지 장치의 write cache와 클라우드 스토리지 보장까지 함께 확인해야 한다.
sequenceDiagram
participant C as 클라이언트
participant S as MySQL 서버
participant I as InnoDB
participant B as Binary Log
C->>S: COMMIT
S->>I: prepare
I-->>S: prepare 완료
S->>B: 이벤트 flush와 sync
B-->>S: 내구성 지점 통과
S->>I: commit
I-->>S: commit 완료
S-->>C: 성공 응답
4. 현재 설정과 관측 지점 확인
먼저 Binary Log 활성화 여부, 이벤트 형식, row image, 보존 기간, GTID 설정을 한 번에 확인한다.
SELECT VERSION() AS mysql_version,
@@global.log_bin AS log_bin,
@@global.binlog_format AS binlog_format,
@@global.binlog_row_image AS binlog_row_image,
@@global.binlog_expire_logs_seconds AS expire_seconds,
@@global.gtid_mode AS gtid_mode,
@@global.enforce_gtid_consistency AS enforce_gtid_consistency;
실행 결과(MySQL 8.0.x):
mysql> SELECT VERSION() AS mysql_version,
-> @@global.log_bin AS log_bin,
-> @@global.binlog_format AS binlog_format,
-> @@global.binlog_row_image AS binlog_row_image,
-> @@global.binlog_expire_logs_seconds AS expire_seconds,
-> @@global.gtid_mode AS gtid_mode,
-> @@global.enforce_gtid_consistency AS enforce_gtid_consistency;
+---------------+---------+---------------+------------------+----------------+-----------+--------------------------+
| mysql_version | log_bin | binlog_format | binlog_row_image | expire_seconds | gtid_mode | enforce_gtid_consistency |
+---------------+---------+---------------+------------------+----------------+-----------+--------------------------+
| 8.0.46 | 0 | ROW | FULL | 2592000 | OFF | OFF |
+---------------+---------+---------------+------------------+----------------+-----------+--------------------------+
1 row in set (0.00 sec)
검증용 컨테이너는 의도적으로 skip-log-bin으로 시작하므로 결과의 log_bin=0은 예제 환경의 값이다. 운영 서버에서는 기대값과 비교해야 한다. binlog_format과 binlog_row_image는 Binary Log가 꺼져 있어도 설정값 자체가 표시될 수 있으므로, 형식만 보고 활성화되었다고 판단하면 안 된다.
트랜잭션이 Binary Log cache를 얼마나 사용하고 디스크 임시 파일로 넘치는지 확인하려면 Performance Schema의 전역 상태를 사용할 수 있다.
SELECT VARIABLE_NAME, VARIABLE_VALUE
FROM performance_schema.global_status
WHERE VARIABLE_NAME IN (
'Binlog_cache_use',
'Binlog_cache_disk_use',
'Binlog_stmt_cache_use',
'Binlog_stmt_cache_disk_use'
)
ORDER BY VARIABLE_NAME;
실행 결과(MySQL 8.0.x):
mysql> SELECT VARIABLE_NAME, VARIABLE_VALUE
-> FROM performance_schema.global_status
-> WHERE VARIABLE_NAME IN (
-> 'Binlog_cache_use',
-> 'Binlog_cache_disk_use',
-> 'Binlog_stmt_cache_use',
-> 'Binlog_stmt_cache_disk_use'
-> )
-> ORDER BY VARIABLE_NAME;
+----------------------------+----------------+
| VARIABLE_NAME | VARIABLE_VALUE |
+----------------------------+----------------+
| Binlog_cache_disk_use | 0 |
| Binlog_cache_use | 0 |
| Binlog_stmt_cache_disk_use | 0 |
| Binlog_stmt_cache_use | 0 |
+----------------------------+----------------+
4 rows in set (0.01 sec)
Binlog_cache_disk_use가 지속해서 빠르게 증가하면 큰 트랜잭션, 긴 트랜잭션 또는 binlog_cache_size 부족을 의심할 수 있다. 그러나 이 누적값 하나만 보고 즉시 cache를 확대해서는 안 된다. 증가율, 동시 트랜잭션 수, 임시 파일 I/O, 트랜잭션 크기 분포를 함께 확인해야 한다. 세션마다 필요한 cache가 커질 수 있으므로 과도한 확대는 메모리 위험으로 바뀔 수 있다.
5. 역할 1: 복제의 원본 변경 스트림
비동기 복제에서 소스는 Binary Log 이벤트를 replica에 전달한다. replica의 receiver thread는 이벤트를 Relay Log에 저장하고, applier thread가 이를 실행한다. 다중 스레드 적용을 사용하면 여러 트랜잭션이 병렬로 처리될 수 있지만 의존 관계와 commit order가 병렬성의 한계를 결정한다.
복제 운영에서 Binary Log는 다음과 같은 의미를 가진다.
- 소스가 필요한 파일을 이미 삭제하면 지연된 replica는 이어서 받을 수 없다.
- 큰 트랜잭션은 전송·Relay Log 기록·적용을 한꺼번에 지연시킬 수 있다.
ROW이벤트는 스키마가 호환되어야 하며 DDL 순서도 보존되어야 한다.- GTID는 실행된 트랜잭션 집합을 식별하고 자동 위치 탐색과 장애 조치를 단순화한다.
- replica의 Binary Log를 켜고
log_replica_updates를 사용하면 계층형 복제와 승격 후 연속성이 가능하지만, 저장 공간과 I/O 비용이 증가한다.
Binary Log 보존 기간은 단순한 디스크 정리 값이 아니다. 허용 가능한 최대 복제 지연 시간과 장애 복구 시간보다 길어야 한다. 예를 들어 주말 동안 중단될 수 있는 replica가 있다면 평일 평균 지연만 기준으로 보존 기간을 정해서는 안 된다.
6. 역할 2: 전체 백업 이후의 시점 복구
PITR은 보통 다음 두 자산을 결합한다.
- 특정 시점에 일관되게 생성한 전체 백업
- 백업 기준 위치 또는 GTID 이후부터 복구 목표 직전까지의 Binary Log
복구 절차는 새 환경에서 전체 백업을 복원한 뒤, 필요한 Binary Log 범위를 시간·position·GTID 기준으로 선별해 재생하는 방식이다. mysqlbinlog은 파일을 사람이 읽을 수 있는 형태로 해석하고 원격 서버에서 로그를 가져오거나 재생 스트림을 생성할 때 사용한다.
다음은 형식을 보여 주는 운영 명령 예시다. 파일명과 시간은 실제 백업 메타데이터에 맞추어야 하며, 복구 대상은 운영 소스가 아닌 격리된 복구 인스턴스를 우선한다.
mysqlbinlog \
--read-from-remote-server \
--host=db.example.invalid \
--user=binlog_reader \
--raw \
--stop-never \
mysql-bin.000125
mysqlbinlog \
--start-datetime='2026-08-31 08:00:00' \
--stop-datetime='2026-08-31 08:42:00' \
mysql-bin.000125 mysql-bin.000126 \
> recovery-window.sql
시간 기준은 편리하지만 경계가 모호할 수 있다. 같은 초에 여러 트랜잭션이 커밋될 수 있고 서버 시간대 해석도 개입한다. 실제 사고 대응에서는 전체 백업의 Binary Log 좌표 또는 GTID, 삭제·오입력 트랜잭션의 정확한 이벤트 경계, 재생 종료 지점을 함께 검증한다.
6.1 PITR이 실패하는 전형적인 이유
- 전체 백업은 있지만 그 이후 Binary Log가 보존되지 않았다.
- 로그 파일은 있으나 백업이 어느 position·GTID에서 생성되었는지 모른다.
- 복구 리허설 없이
mysqlbinlog | mysql을 운영 서버에 직접 실행한다. - 시간대 또는
--start-datetime·--stop-datetime경계를 잘못 해석한다. - Binary Log 암호화 키나 백업 암호화 키를 함께 복구하지 못한다.
- 오브젝트 스토리지로 이관한 로그의 연속성과 checksum을 검증하지 않았다.
따라서 PITR 가능 여부는 “Binary Log를 켰다”가 아니라 백업 기준점부터 목표 시점까지 연속된 로그를 실제로 복구 환경에서 재생할 수 있다는 조건으로 판정해야 한다.
7. 역할 3: 감사 추적의 단서와 한계
Binary Log는 변경 감사에 유용하지만 완전한 감사 시스템은 아니다.
ROW 형식은 어떤 row가 변경되었는지 보여 줄 수 있으나 원래 SQL 문장이나 업무 요청의 목적을 항상 복원하지는 못한다. 일반 SELECT는 데이터 변경이 아니므로 Binary Log에 기록되지 않는다. 접속 실패, 권한 조회, 실행됐지만 row를 바꾸지 않은 문장도 감사 요구와 Binary Log 범위가 다를 수 있다. 계정 정보 역시 이벤트와 환경에 따라 충분하지 않을 수 있으며, 애플리케이션이 공용 DB 계정을 쓰면 최종 사용자를 식별할 수 없다.
따라서 감사 설계에서는 다음 계층을 분리한다.
- Binary Log: 커밋된 데이터·DDL 변경의 재생 가능한 근거
- MySQL Enterprise Audit 또는 호환 감사 기능: 접속, 문장 실행, 정책 대상 이벤트
- 애플리케이션 감사 로그: 최종 사용자, 요청 ID, 업무 행위, 승인 맥락
- 인프라 로그: 관리 API, 파라미터 변경, 백업·복원·장애 조치 이력
Binary Log를 감사 목적으로 외부 보관할 때는 접근 통제와 암호화가 중요하다. row event에는 개인정보나 민감한 업무 값이 포함될 수 있다. 읽기 권한을 최소화하고 전송·보관 암호화, 보존·파기 정책, 접근 감사, 무결성 검증을 함께 설계해야 한다.
8. 보존, 회전, 용량 관리
Binary Log 파일은 크기와 시간에 따라 회전되고, 만료 정책에 의해 제거된다. 주요 설계 변수는 다음과 같다.
| 항목 | 운영 질문 |
|---|---|
binlog_expire_logs_seconds |
가장 오래 중단될 replica와 PITR 보존 목표보다 충분히 긴가? |
max_binlog_size |
파일 단위 전송·아카이빙·복구 작업에 적절한가? |
| 저장 공간 | 평상시뿐 아니라 대량 배치·DDL 기간의 생성률도 감당하는가? |
| 외부 아카이브 | 파일 연속성, checksum, 암호화 키, 복원 절차가 검증되었는가? |
| 수동 정리 | 활성 replica가 필요로 하는 로그를 삭제하지 않는가? |
max_binlog_size는 엄격한 트랜잭션 분할 한도가 아니다. 하나의 큰 트랜잭션은 파일 크기 목표보다 큰 이벤트 집합을 만들 수 있다. 따라서 파일 크기만 제한해 거대 트랜잭션 위험을 해결할 수 없다.
수동 삭제가 필요하면 파일 시스템에서 직접 rm으로 지우지 않는다. MySQL이 관리하는 index와 상태를 일치시키기 위해 PURGE BINARY LOGS를 사용하되, 모든 replica의 수신 위치와 백업 기준점을 먼저 확인한다. 자동 만료와 수동 삭제가 겹치는 환경에서는 운영 절차의 책임 주체를 명확히 해야 한다.
9. 성능과 장애 위험
Binary Log 비용은 단순 디스크 용량에 그치지 않는다.
9.1 커밋 지연
sync_binlog=1은 커밋 내구성을 강화하지만 storage fsync 지연이 commit latency에 반영된다. group commit이 이를 완화하더라도 느린 저장 장치, burst credit 고갈, 파일 시스템 정체는 전체 쓰기 처리량을 흔들 수 있다. 지연을 줄이려고 sync 주기를 완화하면 OS 또는 호스트 장애에서 최근 이벤트를 잃을 수 있으므로, Redo Log 설정과 복제·PITR 요구를 함께 판단한다.
9.2 큰 트랜잭션
큰 트랜잭션은 Binary Log cache의 디스크 spill, 큰 이벤트 전송, replica 적용 지연, 복구 시간 증가를 동시에 유발한다. 대량 변경은 작은 단위로 나누되 업무 원자성, 중간 상태 노출, 재시도 안전성을 함께 설계해야 한다.
9.3 디스크 가득 참
Binary Log가 데이터 디스크를 채우면 새 트랜잭션 기록과 서버 운영이 중단될 수 있다. 사용량 경보만으로는 늦을 수 있으므로 생성 속도와 남은 시간(time-to-full)을 관찰하고, 외부 아카이브 성공 여부와 replica 지연을 같은 대시보드에서 연결하는 편이 좋다.
9.4 잘못된 만료 정책
보존 기간을 짧게 하면 공간은 절약되지만, 장시간 중단된 replica를 다시 만드는 비용과 PITR 공백이 커진다. 반대로 무제한 보존은 장애 시점에 디스크를 소진한다. 복구 목표와 실제 일일 생성량을 기준으로 정책을 수치화해야 한다.
10. Aurora MySQL에서의 해석
Aurora MySQL은 Community MySQL과 호환되는 Binary Log 기능을 제공하지만, 기본 복제와 복구 구조는 다르다.
- 같은 Aurora cluster 안의 writer와 reader는 MySQL Binary Log가 아니라 Aurora의 분산 스토리지와 cluster replication 경로를 사용한다.
- 자동 백업과 특정 시점 복원은 Aurora 관리형 백업·스토리지 기능이 담당한다. cluster 내부 PITR을 위해 사용자가 Binary Log 파일을 직접 모아 재생하는 방식이 기본은 아니다.
- 외부 MySQL로 논리 복제하거나 AWS DMS·CDC 소비자가 필요하면 Binary Log 관련 cluster parameter와 보존 설정을 별도로 검토해야 한다.
- Binary Log를 활성화하면 writer에 추가 CPU·I/O·커밋 비용이 생길 수 있으므로 “reader가 있으니 필요하다”는 이유만으로 켜서는 안 된다.
- 장애 조치 후에도 외부 소비자가 올바른 endpoint와 GTID·position에서 이어받는지 검증해야 한다.
즉 Aurora에서는 cluster 내부 고가용성·PITR과 외부 논리 변경 스트림을 분리해서 설계한다. 전자는 Aurora 관리 기능의 보존 기간과 복원 리허설이 중심이고, 후자는 Binary Log 활성화, 외부 소비자 지연, 장애 조치 연속성, 추가 비용이 중심이다.
11. 운영 점검표
활성화와 형식
-
log_bin -
binlog_format과binlog_row_image - GTID 사용 여부와
enforce_gtid_consistency -
sync_binlog
보존과 복구
성능과 용량
-
Binlog_cache_disk_use
보안과 감사
12. 결론
Binary Log는 MySQL 변경 이력을 복제 가능한 이벤트 스트림으로 바꾸는 서버 계층의 핵심 기능이다. 복제에서는 replica가 따라갈 원본이 되고, PITR에서는 전체 백업 이후의 시간을 복원하는 재생 기록이 되며, 감사에서는 커밋된 변경을 조사하는 중요한 단서가 된다. 그러나 일반 조회 감사까지 포괄하지 않으며, 보존 기간·내구성·암호화·복구 기준점이 함께 관리되지 않으면 세 역할 모두 쉽게 무너진다.
운영자는 Binary Log 활성화 여부만 확인해서는 안 된다. 커밋 경로의 내구성, 이벤트 형식, GTID, 생성률, replica 지연, 백업 기준점, 외부 아카이브, 감사 계층을 하나의 수명 주기로 관리해야 한다. 후속 기술노트에서는 Binary Log format별 동작 차이와 row event 해석, GTID 기반 복제 위치 관리, mysqlbinlog을 이용한 실제 시점 복구 절차를 더 구체적으로 다룬다.