Undo Log와 Redo Log — 크래시 복구의 핵심 메커니즘
데이터를 변경한 직후 서버가 갑자기 꺼지면, 커밋한 데이터는 안전할까요? 커밋하지 않은 데이터는 어떻게 되돌릴 수 있을까요?
데이터베이스에서 가장 중요한 약속은 커밋한 데이터는 반드시 유지되고, 커밋하지 않은 데이터는 반드시 되돌려진다는 것입니다. InnoDB는 이 약속을 Redo Log와 Undo Log로 지킵니다.
개념 정의
- Redo Log: 커밋된 변경 사항을 기록하여, 크래시 후 재적용(Redo) 에 사용
- Undo Log: 변경 이전의 값을 기록하여, 되돌리기(Undo) 와 MVCC에 사용
트랜잭션 실행:
1. Undo Log에 이전 값 기록 (되돌리기 준비)
2. Buffer Pool에서 데이터 변경 (메모리)
3. Redo Log에 변경 내용 기록 (디스크에 플러시)
4. COMMIT → Redo Log 디스크 동기화
5. (나중에) Buffer Pool의 더티 페이지를 디스크에 기록
WAL (Write-Ahead Logging)
WAL은 InnoDB의 데이터 안전성을 보장하는 핵심 원칙입니다.
"데이터를 변경하기 전에, 반드시 로그를 먼저 디스크에 기록한다"
WAL 없이:
데이터 페이지를 직접 디스크에 쓰는 중 크래시 → 부분 쓰기(Partial Write) → 데이터 손상
WAL 있을 때:
Redo Log를 먼저 디스크에 기록 → 크래시 → Redo Log 기반으로 복구
WAL이 효율적인 이유:
- Redo Log는 순차 쓰기 (Sequential Write) → 빠름
- 데이터 페이지는 랜덤 쓰기 (Random Write) → 느림
- 순차 쓰기는 랜덤 쓰기보다 수십~수백 배 빠릅니다
Redo Log 상세
구조
ib_logfile0 ←→ ib_logfile1 (순환 쓰기)
┌────────────────────────────────────────┐
│ ████████████████░░░░░░░░░░░░░░░░░░░░ │
│ ↑ ↑ ↑ │
│ checkpoint_lsn write_pos end │
└────────────────────────────────────────┘
████: 아직 디스크에 플러시되지 않은 더티 페이지의 redo 기록
░░░░: 재사용 가능한 공간 (체크포인트 이전)
- Redo Log는 고정 크기 파일을 순환하여 사용합니다
write_pos: 현재 쓰기 위치checkpoint_lsn: 여기까지의 더티 페이지는 디스크에 플러시 완료
LSN (Log Sequence Number)
LSN은 Redo Log의 논리적 위치를 나타내는 단조 증가 값입니다.
LSN 100: page A 변경
LSN 150: page B 변경
LSN 200: page A 변경
LSN 250: COMMIT
LSN 300: page C 변경
...
-- 현재 LSN 확인
SHOW ENGINE INNODB STATUS\G
-- Log sequence number: 현재 LSN
-- Log flushed up to: 디스크에 플러시된 LSN
-- Pages flushed up to: 체크포인트 LSN
-- Last checkpoint at: 마지막 체크포인트 LSN
Redo Log 크기 설정
-- MySQL 8.0.30 이전
-- my.cnf에서 설정 (서버 재시작 필요)
-- innodb_log_file_size = 512M
-- innodb_log_files_in_group = 2
-- 총 Redo Log 크기 = 512M × 2 = 1GB
-- MySQL 8.0.30 이후
-- innodb_redo_log_capacity = 1073741824 (1GB)
SHOW VARIABLES LIKE 'innodb_redo_log_capacity';
Redo Log 크기가 작으면:
- 체크포인트가 자주 발생 → 더티 페이지 플러시 빈번 → I/O 증가
- 쓰기 성능 저하
Redo Log 크기가 크면:
- 체크포인트 빈도 감소 → 정상 운영 시 성능 향상
- 크래시 복구 시간이 길어질 수 있음
innodb_flush_log_at_trx_commit
이 설정은 ACID의 Durability에 직접적인 영향을 미칩니다.
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
| 값 | 동작 | 안전성 | 성능 |
|---|---|---|---|
| 1 (기본) | 커밋마다 fsync | 데이터 손실 없음 | 가장 느림 |
| 2 | 커밋마다 OS 버퍼에 쓰기, 1초마다 fsync | OS 크래시 시 최대 1초 손실 | 중간 |
| 0 | 1초마다 Log Buffer → 디스크 | MySQL 크래시 시 최대 1초 손실 | 가장 빠름 |
값=1: Log Buffer → Redo Log 파일 → fsync (커밋마다)
값=2: Log Buffer → OS Page Cache (커밋마다) → fsync (1초마다)
값=0: Log Buffer → OS Page Cache → fsync (1초마다)
금융/결제 시스템: 반드시 1
일반 웹 서비스: 1 또는 2
임시 데이터/로그: 0도 가능
Undo Log 상세
역할
Undo Log는 두 가지 역할을 합니다.
- 트랜잭션 롤백: 커밋하지 않은 변경을 되돌림
- MVCC: 읽기 트랜잭션에 이전 버전의 데이터 제공
종류
INSERT Undo Log:
INSERT된 행의 PK를 기록 → 롤백 시 DELETE
→ 트랜잭션 커밋 후 즉시 삭제 가능 (MVCC에 불필요)
UPDATE Undo Log:
UPDATE/DELETE의 이전 값을 기록 → 롤백 시 이전 값 복원
→ 다른 트랜잭션의 MVCC에 필요할 수 있어 즉시 삭제 불가
Undo 테이블스페이스
-- MySQL 8.0: Undo Log가 별도 테이블스페이스에 저장
-- undo_001, undo_002 파일
-- Undo 테이블스페이스 확인
SELECT TABLESPACE_NAME, FILE_NAME, FILE_TYPE
FROM information_schema.FILES
WHERE FILE_TYPE = 'UNDO LOG';
-- Undo 테이블스페이스 자동 truncate (8.0.14+)
SHOW VARIABLES LIKE 'innodb_undo_log_truncate'; -- 기본 ON
SHOW VARIABLES LIKE 'innodb_max_undo_log_size'; -- 기본 1GB
Undo Log Purge
커밋된 트랜잭션의 Undo Log는 더 이상 필요하지 않으면 purge 스레드가 정리합니다.
Purge 조건:
해당 Undo Log를 참조하는 Read View가 더 이상 없을 때
→ 모든 활성 트랜잭션의 Read View보다 오래된 Undo Log만 purge 가능
-- History list length: purge 대기 중인 Undo Log 수
SHOW ENGINE INNODB STATUS\G
-- History list length 1234
-- purge 스레드 수 (기본 4)
SHOW VARIABLES LIKE 'innodb_purge_threads';
History list length가 계속 증가하면:
- 장기 트랜잭션이 purge를 막고 있을 가능성
- Undo 테이블스페이스가 비대해짐
- SELECT 성능 저하 (긴 버전 체인 탐색)
체크포인트 (Checkpoint)
체크포인트는 Buffer Pool의 더티 페이지(Dirty Page) 를 디스크에 쓰는 작업입니다.
왜 필요한가
Redo Log는 고정 크기 → 순환 사용
write_pos가 checkpoint_lsn을 따라잡으면 → Redo Log 공간 부족!
→ 체크포인트로 더티 페이지를 플러시하여 Redo Log 공간 확보
체크포인트 종류
Sharp Checkpoint:
모든 더티 페이지를 한꺼번에 플러시
→ 서버 종료 시에만 사용 (정상 종료)
Fuzzy Checkpoint (일반적):
더티 페이지를 조금씩 지속적으로 플러시
→ 정상 운영 중 사용
Fuzzy Checkpoint의 트리거:
- Master Thread: 주기적으로 더티 페이지 플러시
- Page Cleaner Thread: 더티 페이지 비율이 임계값 초과 시
- Adaptive Flushing: Redo Log 사용량에 비례하여 플러시
-- 더티 페이지 비율 확인
SELECT
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_pages_dirty') /
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_pages_total') * 100
AS dirty_page_pct;
-- 관련 설정
SHOW VARIABLES LIKE 'innodb_max_dirty_pages_pct'; -- 기본 90%
SHOW VARIABLES LIKE 'innodb_max_dirty_pages_pct_lwm'; -- 기본 10%
SHOW VARIABLES LIKE 'innodb_adaptive_flushing'; -- 기본 ON
크래시 복구 과정
MySQL이 비정상 종료 후 재시작하면 다음 과정이 실행됩니다.
1. Redo Log 스캔 (체크포인트부터 마지막 기록까지)
2. Redo 적용 (Roll Forward): 커밋된 변경 사항을 데이터 페이지에 재적용
3. Undo 적용 (Roll Back): 커밋되지 않은 트랜잭션의 변경 사항을 되돌림
크래시 시점의 트랜잭션 상태:
trx A: COMMIT 완료 → Redo Log에 기록 있음 → Redo 재적용
trx B: 진행 중 (미커밋) → Undo Log로 롤백
trx C: COMMIT 완료 + 데이터 페이지 플러시 완료 → 아무것도 안 함
-- 복구 시 강제 모드 (긴급 상황)
-- innodb_force_recovery = 1~6 (숫자가 클수록 더 많은 것을 건너뜀)
-- 정상 상황에서는 0 (기본값)
SHOW VARIABLES LIKE 'innodb_force_recovery';
Redo Log와 Undo Log 관계 정리
트랜잭션 시작
│
┌──────────┴──────────┐
│ │
Undo Log 기록 Redo Log 기록
(이전 값 보관) (변경 내용 기록)
│ │
│ │
Buffer Pool에서 Log Buffer →
데이터 변경 Redo Log 파일
│ │
├─── COMMIT ──────────┤
│ │
Undo Log: Redo Log:
MVCC에 사용됨 디스크 동기화 완료
나중에 purge 체크포인트 후 재사용
모니터링 명령어
-- Redo Log 상태
SHOW ENGINE INNODB STATUS\G
-- LOG 섹션 확인
-- Undo Log 상태
SELECT NAME, SUBSYSTEM, COUNT, AVG_COUNT
FROM information_schema.INNODB_METRICS
WHERE NAME LIKE '%undo%' OR NAME LIKE '%purge%';
-- 트랜잭션과 Undo 상태
SELECT * FROM information_schema.INNODB_TRX;
정리
- WAL 원칙: 데이터 변경 전에 반드시 Redo Log를 먼저 디스크에 기록합니다
- Redo Log는 커밋된 변경을 재적용하는 데 사용되며, 고정 크기 파일을 순환하여 씁니다
- Undo Log는 롤백과 MVCC에 사용되며, purge 스레드가 불필요해진 것을 정리합니다
- 체크포인트는 더티 페이지를 디스크에 플러시하여 Redo Log 공간을 확보합니다
innodb_flush_log_at_trx_commit=1이 완전한 Durability를 보장하지만, 성능과의 트레이드오프가 있습니다- 장기 트랜잭션은 Undo Log purge를 막아 성능 저하의 원인이 됩니다
댓글 로딩 중...