Theme:

디스크에 16KB 데이터를 쓰는 도중 전원이 나가면 어떻게 될까요? 절반만 기록된 데이터를 복구할 수 있을까요?

Partial Write 문제

InnoDB는 16KB 크기의 페이지를 기본 단위로 사용합니다. 하지만 대부분의 운영체제와 디스크는 **4KB 단위(섹터)**로 데이터를 기록합니다.

PLAINTEXT
InnoDB 페이지 (16KB)
┌──────┬──────┬──────┬──────┐
│ 4KB  │ 4KB  │ 4KB  │ 4KB  │
│  ①   │  ②   │  ③   │  ④   │
└──────┴──────┴──────┴──────┘

OS가 디스크에 기록하는 단위: 4KB씩 4번

16KB 페이지를 디스크에 쓸 때 OS는 4KB씩 4번에 나눠 기록합니다. 만약 2번째 4KB를 기록하는 도중 서버가 비정상 종료되면 어떻게 될까요?

PLAINTEXT
정상 기록 전:  [구 데이터 16KB 전체]
장애 발생 후:  [새 4KB ①] [새 4KB ② 일부] [구 4KB ③] [구 4KB ④]
               ↑ 새 데이터        ↑ 손상           ↑ 구 데이터

이 상태의 페이지는 완전히 망가진 상태입니다. 새 데이터도 구 데이터도 아닌, 사용할 수 없는 상태가 됩니다.

Redo Log만으로는 복구할 수 없는 이유

"Redo Log가 있으니 복구할 수 있지 않나요?"라고 생각할 수 있습니다. 하지만 Redo Log는 정상적인 페이지에 변경을 재적용하는 것입니다.

PLAINTEXT
Redo Log: "페이지 X의 offset 100 위치에 값 42를 기록"

이 로그를 적용하려면 페이지 X가 올바른 상태여야 합니다. 부분적으로 기록된 손상 페이지에 Redo Log를 적용하면 데이터가 더 망가집니다.

Doublewrite Buffer란

Doublewrite Buffer는 이 문제를 해결하는 안전장치입니다. 페이지를 데이터 파일에 쓰기 전에 별도의 영역에 먼저 복사해둡니다.

동작 흐름

PLAINTEXT
1. Dirty Page를 Buffer Pool에서 가져옴
2. Doublewrite 영역에 순차적으로 기록 + fsync (디스크에 확실히 저장)
3. 실제 데이터 파일의 해당 위치에 기록

복구 시나리오:
A) 2단계에서 장애 → 데이터 파일의 기존 페이지가 온전 → Redo Log 적용 가능
B) 3단계에서 장애 → Doublewrite 영역의 복사본으로 페이지 복원 → Redo Log 적용
PLAINTEXT
                    Buffer Pool
                    ┌─────────┐
                    │ Dirty   │
                    │ Pages   │
                    └────┬────┘

                    ① 먼저 기록

              Doublewrite Buffer (순차 기록)
              ┌─────────────────────────┐
              │ Page A │ Page B │ Page C │
              └─────────────────────────┘

                    ② 그 다음 기록

              데이터 파일 (랜덤 위치)
              ┌──┐  ┌──┐  ┌──┐
              │ A│  │ B│  │ C│
              └──┘  └──┘  └──┘

복구 과정

서버가 비정상 종료 후 다시 시작되면 InnoDB는 다음을 수행합니다.

  1. 데이터 파일의 각 페이지 체크섬을 검증합니다
  2. 체크섬이 맞지 않으면 (Partial Write 발생) Doublewrite 영역에서 정상 복사본을 가져옵니다
  3. 복원된 페이지에 Redo Log를 적용하여 최신 상태로 만듭니다
PLAINTEXT
복구 흐름:
데이터 파일 페이지 검증
├─ 체크섬 OK → Redo Log 적용
└─ 체크섬 FAIL → Doublewrite에서 복원 → Redo Log 적용

성능 영향

Doublewrite는 모든 페이지를 두 번 쓰기 때문에 "쓰기 양이 2배"로 보일 수 있습니다. 하지만 실제 성능 영향은 그보다 작습니다.

왜 2배보다 적은가

  1. 순차 쓰기: Doublewrite 영역에는 여러 페이지를 모아서 순차적으로 기록합니다. 순차 I/O는 랜덤 I/O보다 훨씬 빠릅니다
  2. 배치 처리: 한 번의 fsync로 여러 페이지를 함께 기록합니다
  3. 실제 오버헤드: 일반적으로 5~10% 정도의 쓰기 성능 감소

SSD에서의 영향

SSD에서는 상황이 조금 다릅니다.

  • SSD는 순차/랜덤 I/O 성능 차이가 적습니다
  • 하지만 SSD의 쓰기 수명(Write Endurance)에 영향을 줍니다
  • Doublewrite로 인한 추가 쓰기가 SSD 수명을 단축시킬 수 있습니다

MySQL 8.0에서의 변경사항

8.0.20 이전

Doublewrite 영역이 시스템 테이블스페이스(ibdata1) 안에 있었습니다.

PLAINTEXT
ibdata1
├─ 시스템 테이블스페이스 데이터
├─ Doublewrite Buffer (2MB, 128페이지)
└─ 기타 시스템 정보

8.0.20 이후

별도의 Doublewrite 파일로 분리되었습니다.

PLAINTEXT
#ib_16384_0.dblwr    -- Doublewrite 파일 1
#ib_16384_1.dblwr    -- Doublewrite 파일 2

분리의 장점은 다음과 같습니다.

  • Doublewrite 파일을 별도 디스크에 배치 가능 (I/O 분산)
  • 시스템 테이블스페이스 크기 감소
  • 병렬 Doublewrite 가능 (여러 Buffer Pool 인스턴스가 각자의 Doublewrite 파일 사용)

관련 설정

SQL
-- Doublewrite 활성화 여부 (기본 ON)
SHOW VARIABLES LIKE 'innodb_doublewrite';

-- Doublewrite 파일 경로 (8.0.20+)
SHOW VARIABLES LIKE 'innodb_doublewrite_dir';

-- Doublewrite 파일 수 (8.0.20+)
SHOW VARIABLES LIKE 'innodb_doublewrite_files';

-- 페이지당 Doublewrite 배치 크기 (8.0.20+)
SHOW VARIABLES LIKE 'innodb_doublewrite_batch_size';

Doublewrite 비활성화를 고려하는 경우

비활성화가 안전한 상황

  1. 원자적 쓰기를 지원하는 파일시스템: ZFS, FusionIO 등 파일시스템 레벨에서 원자적 쓰기를 보장하면 Partial Write가 발생하지 않습니다

  2. 원자적 쓰기를 지원하는 스토리지: 일부 하드웨어가 16KB 원자적 쓰기를 지원합니다

SQL
-- 비활성화
SET GLOBAL innodb_doublewrite = OFF;

-- 또는 my.cnf에서 설정
-- [mysqld]
-- innodb_doublewrite = 0

비활성화가 위험한 상황

  • 일반 ext4, XFS 파일시스템 사용 시
  • 전원 장애가 발생할 수 있는 환경
  • 데이터 손실이 허용되지 않는 운영 환경

Doublewrite vs Redo Log vs Undo Log

세 가지가 각각 다른 목적을 가지고 있습니다.

구분목적보호 대상
DoublewritePartial Write 방지페이지의 물리적 무결성
Redo Log커밋된 변경 복구 (Durability)트랜잭션의 논리적 무결성
Undo Log롤백, MVCC트랜잭션 취소, 읽기 일관성
PLAINTEXT
장애 복구 흐름:
1. Doublewrite로 손상된 페이지 복원 (물리적 복구)
2. Redo Log로 커밋된 트랜잭션 재적용 (논리적 복구)
3. Undo Log로 미커밋 트랜잭션 롤백 (일관성 복구)

모니터링

SQL
-- Doublewrite 통계
SHOW STATUS LIKE 'Innodb_dblwr%';
변수설명
Innodb_dblwr_pages_writtenDoublewrite에 기록된 총 페이지 수
Innodb_dblwr_writesDoublewrite 영역에 기록한 횟수
SQL
-- 배치당 평균 페이지 수 계산
SELECT
    Innodb_dblwr_pages_written / Innodb_dblwr_writes AS pages_per_write
FROM (
    SELECT
        (SELECT VARIABLE_VALUE FROM performance_schema.global_status
         WHERE VARIABLE_NAME = 'Innodb_dblwr_pages_written') AS Innodb_dblwr_pages_written,
        (SELECT VARIABLE_VALUE FROM performance_schema.global_status
         WHERE VARIABLE_NAME = 'Innodb_dblwr_writes') AS Innodb_dblwr_writes
) t;

이 값이 1에 가까우면 페이지가 하나씩 기록되고 있다는 의미이고, 값이 높을수록 효율적인 배치 처리가 이루어지고 있다는 의미입니다.

정리

  • Doublewrite Buffer는 16KB 페이지의 부분 쓰기(Partial Write) 문제를 방지합니다
  • 페이지를 먼저 Doublewrite 영역에 순차 기록하고, 그 다음 데이터 파일에 기록합니다
  • 장애 복구 시 손상된 페이지를 Doublewrite 영역에서 복원한 후 Redo Log를 적용합니다
  • MySQL 8.0.20부터 별도 파일로 분리되어 성능이 개선되었습니다
  • 원자적 쓰기를 지원하는 파일시스템에서만 비활성화를 고려해야 합니다
댓글 로딩 중...