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

데이터베이스에서 가장 중요한 약속은 커밋한 데이터는 반드시 유지 되고, 커밋하지 않은 데이터는 반드시 되돌려진다 는 것입니다. InnoDB는 이 약속을 Redo Log 와 Undo 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;

주의할 점

Redo Log가 너무 작으면 체크포인트 폭풍이 발생한다

Redo Log 크기가 작으면 write_poscheckpoint_lsn을 빠르게 따라잡고, 그때마다 긴급 체크포인트가 발생하여 더티 페이지를 강제 플러시합니다. 쓰기 성능이 급격히 떨어지는 원인입니다. Redo Log 크기는 1시간 분량의 쓰기를 담을 수 있을 정도로 설정하는 것이 일반적입니다.

innodb_flush_log_at_trx_commit 변경 시 데이터 손실 가능

값 0이나 2로 변경하면 최대 1초 분량의 커밋된 데이터가 유실될 수 있습니다. 금융/결제 시스템에서는 반드시 1을 유지해야 하고, 성능이 필요한 임시 데이터에만 다른 값을 고려합니다.

Undo 테이블스페이스가 비대해지면 수축시키기 어렵다

장기 트랜잭션으로 인해 Undo 테이블스페이스가 한 번 커지면, purge가 완료되어도 자동으로 크기가 줄어들지 않습니다. MySQL 8.0.14의 innodb_undo_log_truncate 옵션이 이를 자동화하지만, 최소 2개의 Undo 테이블스페이스가 필요합니다.

정리

항목설명
WAL 원칙데이터 변경 전에 Redo Log를 먼저 디스크에 기록
Redo Log커밋된 변경 재적용, 고정 크기 파일 순환 사용
Undo Log롤백 + MVCC용, purge 스레드가 정리
체크포인트더티 페이지를 디스크에 플러시하여 Redo Log 공간 확보
flush_log_at_trx_commit1=완전 안전(느림), 2=OS 크래시만 위험, 0=가장 빠름(위험)
장기 트랜잭션 위험Undo Log purge 차단 → 디스크 증가 + 조회 성능 저하
댓글 로딩 중...