Doublewrite Buffer — 부분 쓰기 방지와 데이터 무결성
디스크에 16KB 데이터를 쓰는 도중 전원이 나가면 어떻게 될까요? 절반만 기록된 데이터를 복구할 수 있을까요?
Partial Write 문제
InnoDB는 16KB 크기의 페이지를 기본 단위로 사용합니다. 하지만 대부분의 운영체제와 디스크는 **4KB 단위(섹터)**로 데이터를 기록합니다.
InnoDB 페이지 (16KB)
┌──────┬──────┬──────┬──────┐
│ 4KB │ 4KB │ 4KB │ 4KB │
│ ① │ ② │ ③ │ ④ │
└──────┴──────┴──────┴──────┘
OS가 디스크에 기록하는 단위: 4KB씩 4번
16KB 페이지를 디스크에 쓸 때 OS는 4KB씩 4번에 나눠 기록합니다. 만약 2번째 4KB를 기록하는 도중 서버가 비정상 종료되면 어떻게 될까요?
정상 기록 전: [구 데이터 16KB 전체]
장애 발생 후: [새 4KB ①] [새 4KB ② 일부] [구 4KB ③] [구 4KB ④]
↑ 새 데이터 ↑ 손상 ↑ 구 데이터
이 상태의 페이지는 완전히 망가진 상태입니다. 새 데이터도 구 데이터도 아닌, 사용할 수 없는 상태가 됩니다.
Redo Log만으로는 복구할 수 없는 이유
"Redo Log가 있으니 복구할 수 있지 않나요?"라고 생각할 수 있습니다. 하지만 Redo Log는 정상적인 페이지에 변경을 재적용하는 것입니다.
Redo Log: "페이지 X의 offset 100 위치에 값 42를 기록"
이 로그를 적용하려면 페이지 X가 올바른 상태여야 합니다. 부분적으로 기록된 손상 페이지에 Redo Log를 적용하면 데이터가 더 망가집니다.
Doublewrite Buffer란
Doublewrite Buffer는 이 문제를 해결하는 안전장치입니다. 페이지를 데이터 파일에 쓰기 전에 별도의 영역에 먼저 복사해둡니다.
동작 흐름
1. Dirty Page를 Buffer Pool에서 가져옴
2. Doublewrite 영역에 순차적으로 기록 + fsync (디스크에 확실히 저장)
3. 실제 데이터 파일의 해당 위치에 기록
복구 시나리오:
A) 2단계에서 장애 → 데이터 파일의 기존 페이지가 온전 → Redo Log 적용 가능
B) 3단계에서 장애 → Doublewrite 영역의 복사본으로 페이지 복원 → Redo Log 적용
Buffer Pool
┌─────────┐
│ Dirty │
│ Pages │
└────┬────┘
│
① 먼저 기록
▼
Doublewrite Buffer (순차 기록)
┌─────────────────────────┐
│ Page A │ Page B │ Page C │
└─────────────────────────┘
│
② 그 다음 기록
▼
데이터 파일 (랜덤 위치)
┌──┐ ┌──┐ ┌──┐
│ A│ │ B│ │ C│
└──┘ └──┘ └──┘
복구 과정
서버가 비정상 종료 후 다시 시작되면 InnoDB는 다음을 수행합니다.
- 데이터 파일의 각 페이지 체크섬을 검증합니다
- 체크섬이 맞지 않으면 (Partial Write 발생) Doublewrite 영역에서 정상 복사본을 가져옵니다
- 복원된 페이지에 Redo Log를 적용하여 최신 상태로 만듭니다
복구 흐름:
데이터 파일 페이지 검증
├─ 체크섬 OK → Redo Log 적용
└─ 체크섬 FAIL → Doublewrite에서 복원 → Redo Log 적용
성능 영향
Doublewrite는 모든 페이지를 두 번 쓰기 때문에 "쓰기 양이 2배"로 보일 수 있습니다. 하지만 실제 성능 영향은 그보다 작습니다.
왜 2배보다 적은가
- 순차 쓰기: Doublewrite 영역에는 여러 페이지를 모아서 순차적으로 기록합니다. 순차 I/O는 랜덤 I/O보다 훨씬 빠릅니다
- 배치 처리: 한 번의 fsync로 여러 페이지를 함께 기록합니다
- 실제 오버헤드: 일반적으로 5~10% 정도의 쓰기 성능 감소
SSD에서의 영향
SSD에서는 상황이 조금 다릅니다.
- SSD는 순차/랜덤 I/O 성능 차이가 적습니다
- 하지만 SSD의 쓰기 수명(Write Endurance)에 영향을 줍니다
- Doublewrite로 인한 추가 쓰기가 SSD 수명을 단축시킬 수 있습니다
MySQL 8.0에서의 변경사항
8.0.20 이전
Doublewrite 영역이 시스템 테이블스페이스(ibdata1) 안에 있었습니다.
ibdata1
├─ 시스템 테이블스페이스 데이터
├─ Doublewrite Buffer (2MB, 128페이지)
└─ 기타 시스템 정보
8.0.20 이후
별도의 Doublewrite 파일로 분리되었습니다.
#ib_16384_0.dblwr -- Doublewrite 파일 1
#ib_16384_1.dblwr -- Doublewrite 파일 2
분리의 장점은 다음과 같습니다.
- Doublewrite 파일을 별도 디스크에 배치 가능 (I/O 분산)
- 시스템 테이블스페이스 크기 감소
- 병렬 Doublewrite 가능 (여러 Buffer Pool 인스턴스가 각자의 Doublewrite 파일 사용)
관련 설정
-- 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 비활성화를 고려하는 경우
비활성화가 안전한 상황
-
원자적 쓰기를 지원하는 파일시스템: ZFS, FusionIO 등 파일시스템 레벨에서 원자적 쓰기를 보장하면 Partial Write가 발생하지 않습니다
-
원자적 쓰기를 지원하는 스토리지: 일부 하드웨어가 16KB 원자적 쓰기를 지원합니다
-- 비활성화
SET GLOBAL innodb_doublewrite = OFF;
-- 또는 my.cnf에서 설정
-- [mysqld]
-- innodb_doublewrite = 0
비활성화가 위험한 상황
- 일반 ext4, XFS 파일시스템 사용 시
- 전원 장애가 발생할 수 있는 환경
- 데이터 손실이 허용되지 않는 운영 환경
Doublewrite vs Redo Log vs Undo Log
세 가지가 각각 다른 목적을 가지고 있습니다.
| 구분 | 목적 | 보호 대상 |
|---|---|---|
| Doublewrite | Partial Write 방지 | 페이지의 물리적 무결성 |
| Redo Log | 커밋된 변경 복구 (Durability) | 트랜잭션의 논리적 무결성 |
| Undo Log | 롤백, MVCC | 트랜잭션 취소, 읽기 일관성 |
장애 복구 흐름:
1. Doublewrite로 손상된 페이지 복원 (물리적 복구)
2. Redo Log로 커밋된 트랜잭션 재적용 (논리적 복구)
3. Undo Log로 미커밋 트랜잭션 롤백 (일관성 복구)
모니터링
-- Doublewrite 통계
SHOW STATUS LIKE 'Innodb_dblwr%';
| 변수 | 설명 |
|---|---|
| Innodb_dblwr_pages_written | Doublewrite에 기록된 총 페이지 수 |
| Innodb_dblwr_writes | Doublewrite 영역에 기록한 횟수 |
-- 배치당 평균 페이지 수 계산
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부터 별도 파일로 분리되어 성능이 개선되었습니다
- 원자적 쓰기를 지원하는 파일시스템에서만 비활성화를 고려해야 합니다