MySQL은 데이터 무결성과 내결함성을 보장하기 위해 다중 레이어의 로그 시스템을 활용합니다. 주요 구성 요소로는 InnoDB 스토리지 엔진이 관리하는 redo log(리두 로그), 트랜잭션 롤백 및 MVCC를 위한 undo log, 그리고 서버 레이어에서 동작하는 binlog(바이너리 로그)가 있습니다. 본 문서에서는 undo log를 제외하고 redo log와 binlog의 작동 원리, 그들의 차이점, 그리고 두 로그가 어떻게 협업하여 데이터 일관성을 유지하는지를 심층 분석합니다.
예시로 사용할 테이블은 다음과 같습니다:
CREATE TABLE T (
id INT PRIMARY KEY,
value INT
);
다음과 같은 간단한 업데이트 쿼리를 실행한다고 가정합시다:
UPDATE T SET value = value + 1 WHERE id = 2;
이 쿼리는 단순해 보이지만, 내부적으로는 스토리지 엔진과 서버 레이어 간 복잡한 절차를 거칩니다. 특히 데이터 변경 시 발생하는 로깅 메커니즘이 핵심입니다.
1. Redo Log: 인내성 있는 데이터 갱신을 위한 기반
InnoDB는 모든 데이터 변경 사항을 즉시 디스크에 기록하지 않고, 먼저 메모리 상의 버퍼 풀(buffer pool)에서 처리합니다. 이는 디스크 I/O 비용을 줄이고 성능을 극대화하기 위함입니다. 그러나 메모리 기반 작업은 시스템 충돌 시 데이터 손실 위험이 있습니다. 이를 해결하는 것이 바로 redo log입니다.
Write-Ahead Logging (WAL) 전략
Redo log는 WAL(선기입 로깅) 전략을 따릅니다. 즉, 실제 데이터 페이지를 디스크에 쓰기 전에 먼저 변경 내용을 영구 저장 가능한 로그 파일에 기록합니다. 이 방식은 다음과 같은 장점을 제공합니다:
- 데이터 페이지 접근을 위한 랜덤 I/O보다 순차적 로그 기록이 훨씬 효율적입니다.
- 서버 다운 시 재시작 후, 미반영된 변경 사항을 redo log를 통해 복구할 수 있습니다.
순환형 로그 구조
Redo log는 고정 크기의 파일 집합으로 구성되며, 순환(circular) 방식으로 기록됩니다. 예를 들어 4개의 1GB 파일로 구성된 경우, 총 4GB의 기록 공간을 제공합니다. 기록 포인터(write position)가 끝에 도달하면 처음으로 돌아와 덮어씁니다. 하지만 덮어쓰기 전에는 해당 변경 사항이 이미 디스크에 반영되었는지 확인해야 하며, 이 역할을 하는 것이 checkpoint 메커니즘입니다.
만약 write 포인터가 checkpoint를 따라잡으면, 더 이상의 기록이 불가능해지고, 체크포인트를 앞당겨야 합니다. 이 과정은 "로그가 꽉 찼다(log full)"고 표현하며, 일시적인 성능 저하를 유발할 수 있습니다.
내부 데이터 형식
각 redo 로그 레코드는 다음 정보를 포함합니다:
- type: 1바이트로 로그 종류를 식별합니다.
- space ID: 테이블스페이스 식별자 (압축 형식 사용)
- page number: 영향받은 페이지 번호 (압축 가능)
- payload: 실제 변경 데이터. 복구 시 적절한 함수로 파싱됩니다.
2. Binlog: 논리적 변경 기록과 복제의 기반
Binlog는 MySQL 서버 레이어에서 생성되는 로그로, 모든 스토리지 엔진에 공통적으로 적용됩니다. 초기 MySQL(MyISAM 엔진)에서는 내결함성이 없었으며, binlog는 백업 및 복제 용도로만 사용되었습니다. 이후 InnoDB 도입과 함께 redo log와 binlog의 조합을 통해 crash-safe 환경을 구축하게 되었습니다.
Redo Log vs Binlog: 핵심 차이점
| 특징 | Redo Log | Binlog |
|---|---|---|
| 계층 | InnoDB 엔진 레이어 | 서버 레이어 |
| 유형 | 물리적 로그 (페이지 단위 변경) | 논리적 로그 (SQL 또는 행 변경) |
| 기록 방식 | 순환 기록 (고정 크기) | 추가 기록 (무제한 확장 가능) |
| 용도 | 충돌 복구 (crash recovery) | 복제, PITR (Point-in-Time Recovery) |
Binlog 형식은 크게 세 가지로 나뉩니다:
- STATEMENT: 원본 SQL 문을 기록. 일부 함수나 트리거 사용 시 비결정성 문제 발생 가능
- ROW: 각 행의 실제 변경 내용을 기록. 정확성 높음, 사이즈 큼
- MIXED: 자동으로 적절한 형식 선택
현대 운영 환경에서는 정확성 확보를 위해 ROW 형식을 권장합니다.
Binlog를 이용한 데이터 복구
예를 들어, 오전 10시에 잘못된 UPDATE 문이 실행되었다면, 다음과 같은 절차로 복구할 수 있습니다:
- 오전 9시 상태의 최신 백업을 복원합니다.
- 9시부터 10시 직전까지의 binlog를 추출합니다.
mysqlbinlog도구를 사용해 해당 로그를 재실행합니다.
이 방식을 PITR(Point-in-Time Recovery)이라고 하며, binlog의 가장 중요한 활용 사례 중 하나입니다.
3. UPDATE 문의 실행 흐름과 Two-Phase Commit
앞서 언급한 UPDATE 문은 내부적으로 다음과 같은 단계를 거칩니다:
- 읽기: 실행 계획자가 스토리지 엔진을 통해 해당 행을 읽습니다 (메모리 또는 디스크).
- 변경: 실행 계획자가 값을 수정하고, 엔진에 변경 요청을 전달합니다.
- Prepare 단계: InnoDB는 변경 내용을 버퍼 풀에 반영하고, redo log에 prepare 상태로 기록합니다.
- Binlog 기록: 서버 레이어가 binlog에 변경 내용을 기록하고, 디스크에 플러시합니다.
- Commit 단계: 실행 계획자가 엔진에 커밋을 요청하면, InnoDB는 redo log를 commit 상태로 변경합니다.
Two-Phase Commit의 중요성
이 프로세스는 2단계 커밋(two-phase commit) 프로토콜을 따르며, redo log와 binlog의 상태 일관성을 보장합니다. 만약 이 메커니즘이 없다면 다음과 같은 문제가 발생할 수 있습니다:
- Redo log만 기록되고 충돌 시: binlog 부재로 인해 복제 서버나 백업이 원본과 불일치하게 됩니다.
- Binlog만 기록되고 충돌 시: redo log에는 없으므로 주 서버는 롤백하지만, 복제 서버는 적용하여 불일치가 발생합니다.
충돌 복구 과정
서버 재시작 시, InnoDB는 다음과 같은 규칙으로 복구를 수행합니다:
- Redo log에서 commit 마킹이 된 트랜잭션 → 무시 (이미 완료됨)
- Redo log에서 prepare 상태인 트랜잭션 → 해당 XID를 기준으로 binlog를 확인
- Binlog에도 기록되어 있으면 → 커밋 완료 처리
- Binlog에 없거나 손상된 경우 → 트랜잭션 롤백
여기서 XID(Transaction ID)는 두 로그를 연결하는 핵심 필드입니다. 이를 통해 복구 과정에서 두 로그의 상태를 비교할 수 있습니다.
Binlog 무결성 검사
MySQL 5.6.2 이후 버전부터는 binlog_checksum 기능이 도입되어, 로그 파일의 손상을 탐지할 수 있습니다. 이는 binlog 재생 시 신뢰성을 크게 향상시킵니다.
4. 설정 추천 및 결론
데이터 안정성을 극대화하기 위해 다음과 같은 설정을 권장합니다:
innodb_flush_log_at_trx_commit = 1: 모든 트랜잭션이 커밋될 때마다 redo log를 디스크에 강제 기록sync_binlog = 1: 모든 트랜잭션이 binlog에 기록된 후 디스크에 플러시
이 두 설정은 성능에 어느 정도 영향을 주지만, 데이터 손실 리스크를 최소화하며, crash-safe 환경을 완성합니다.
결국, MySQL의 로그 아키텍처는 성능과 안정성의 균형을 추구하는 설계의 산물입니다. Redo log는 빠른 반영과 내결함성을, binlog는 복제와 정밀 복구를 가능하게 하며, 두 로그의 협업을 통해 현대적인 데이터베이스 시스템이 요구하는 신뢰성을 실현합니다.