Theme:

데이터를 변경한 직후 서버가 갑자기 꺼지면, 커밋한 데이터는 안전할까요? 커밋하지 않은 데이터는 어떻게 되돌릴 수 있을까요?

데이터베이스에서 가장 중요한 약속은 커밋한 데이터는 반드시 유지되고, 커밋하지 않은 데이터는 반드시 되돌려진다는 것입니다. InnoDB는 이 약속을 Redo LogUndo Log로 지킵니다.

개념 정의

  • Redo Log: 커밋된 변경 사항을 기록하여, 크래시 후 재적용(Redo) 에 사용
  • Undo Log: 변경 이전의 값을 기록하여, 되돌리기(Undo) 와 MVCC에 사용
PLAINTEXT
트랜잭션 실행:
  1. Undo Log에 이전 값 기록 (되돌리기 준비)
  2. Buffer Pool에서 데이터 변경 (메모리)
  3. Redo Log에 변경 내용 기록 (디스크에 플러시)
  4. COMMIT → Redo Log 디스크 동기화
  5. (나중에) Buffer Pool의 더티 페이지를 디스크에 기록

WAL (Write-Ahead Logging)

WAL은 InnoDB의 데이터 안전성을 보장하는 핵심 원칙입니다.

"데이터를 변경하기 전에, 반드시 로그를 먼저 디스크에 기록한다"

PLAINTEXT
WAL 없이:
  데이터 페이지를 직접 디스크에 쓰는 중 크래시 → 부분 쓰기(Partial Write) → 데이터 손상

WAL 있을 때:
  Redo Log를 먼저 디스크에 기록 → 크래시 → Redo Log 기반으로 복구

WAL이 효율적인 이유:

  • Redo Log는 순차 쓰기 (Sequential Write) → 빠름
  • 데이터 페이지는 랜덤 쓰기 (Random Write) → 느림
  • 순차 쓰기는 랜덤 쓰기보다 수십~수백 배 빠릅니다

Redo Log 상세

구조

PLAINTEXT
ib_logfile0 ←→ ib_logfile1 (순환 쓰기)
┌────────────────────────────────────────┐
│ ████████████████░░░░░░░░░░░░░░░░░░░░  │
│ ↑               ↑                  ↑   │
│ checkpoint_lsn  write_pos          end │
└────────────────────────────────────────┘

████: 아직 디스크에 플러시되지 않은 더티 페이지의 redo 기록
░░░░: 재사용 가능한 공간 (체크포인트 이전)
  • Redo Log는 고정 크기 파일을 순환하여 사용합니다
  • write_pos: 현재 쓰기 위치
  • checkpoint_lsn: 여기까지의 더티 페이지는 디스크에 플러시 완료

LSN (Log Sequence Number)

PLAINTEXT
LSN은 Redo Log의 논리적 위치를 나타내는 단조 증가 값입니다.

LSN 100: page A 변경
LSN 150: page B 변경
LSN 200: page A 변경
LSN 250: COMMIT
LSN 300: page C 변경
...
SQL
-- 현재 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 크기 설정

SQL
-- 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에 직접적인 영향을 미칩니다.

SQL
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
동작안전성성능
1 (기본)커밋마다 fsync데이터 손실 없음가장 느림
2커밋마다 OS 버퍼에 쓰기, 1초마다 fsyncOS 크래시 시 최대 1초 손실중간
01초마다 Log Buffer → 디스크MySQL 크래시 시 최대 1초 손실가장 빠름
PLAINTEXT
값=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는 두 가지 역할을 합니다.

  1. 트랜잭션 롤백: 커밋하지 않은 변경을 되돌림
  2. MVCC: 읽기 트랜잭션에 이전 버전의 데이터 제공

종류

PLAINTEXT
INSERT Undo Log:
  INSERT된 행의 PK를 기록 → 롤백 시 DELETE
  → 트랜잭션 커밋 후 즉시 삭제 가능 (MVCC에 불필요)

UPDATE Undo Log:
  UPDATE/DELETE의 이전 값을 기록 → 롤백 시 이전 값 복원
  → 다른 트랜잭션의 MVCC에 필요할 수 있어 즉시 삭제 불가

Undo 테이블스페이스

SQL
-- 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 스레드가 정리합니다.

PLAINTEXT
Purge 조건:
  해당 Undo Log를 참조하는 Read View가 더 이상 없을 때
  → 모든 활성 트랜잭션의 Read View보다 오래된 Undo Log만 purge 가능
SQL
-- 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) 를 디스크에 쓰는 작업입니다.

왜 필요한가

PLAINTEXT
Redo Log는 고정 크기 → 순환 사용
write_pos가 checkpoint_lsn을 따라잡으면 → Redo Log 공간 부족!
→ 체크포인트로 더티 페이지를 플러시하여 Redo Log 공간 확보

체크포인트 종류

PLAINTEXT
Sharp Checkpoint:
  모든 더티 페이지를 한꺼번에 플러시
  → 서버 종료 시에만 사용 (정상 종료)

Fuzzy Checkpoint (일반적):
  더티 페이지를 조금씩 지속적으로 플러시
  → 정상 운영 중 사용

Fuzzy Checkpoint의 트리거:

  • Master Thread: 주기적으로 더티 페이지 플러시
  • Page Cleaner Thread: 더티 페이지 비율이 임계값 초과 시
  • Adaptive Flushing: Redo Log 사용량에 비례하여 플러시
SQL
-- 더티 페이지 비율 확인
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이 비정상 종료 후 재시작하면 다음 과정이 실행됩니다.

PLAINTEXT
1. Redo Log 스캔 (체크포인트부터 마지막 기록까지)
2. Redo 적용 (Roll Forward): 커밋된 변경 사항을 데이터 페이지에 재적용
3. Undo 적용 (Roll Back): 커밋되지 않은 트랜잭션의 변경 사항을 되돌림
PLAINTEXT
크래시 시점의 트랜잭션 상태:
  trx A: COMMIT 완료 → Redo Log에 기록 있음 → Redo 재적용
  trx B: 진행 중 (미커밋) → Undo Log로 롤백
  trx C: COMMIT 완료 + 데이터 페이지 플러시 완료 → 아무것도 안 함
SQL
-- 복구 시 강제 모드 (긴급 상황)
-- innodb_force_recovery = 1~6 (숫자가 클수록 더 많은 것을 건너뜀)
-- 정상 상황에서는 0 (기본값)
SHOW VARIABLES LIKE 'innodb_force_recovery';

Redo Log와 Undo Log 관계 정리

PLAINTEXT
          트랜잭션 시작

    ┌──────────┴──────────┐
    │                     │
 Undo Log 기록          Redo Log 기록
 (이전 값 보관)         (변경 내용 기록)
    │                     │
    │                     │
 Buffer Pool에서         Log Buffer →
 데이터 변경              Redo Log 파일
    │                     │
    ├─── COMMIT ──────────┤
    │                     │
 Undo Log:              Redo Log:
 MVCC에 사용됨            디스크 동기화 완료
 나중에 purge             체크포인트 후 재사용

모니터링 명령어

SQL
-- 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를 막아 성능 저하의 원인이 됩니다
댓글 로딩 중...