이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.
공부 › DB › 저장과 복구
Undo와 Redo 로그
커밋 직후 전원이 꺼져도 데이터가 남는 이유
DB는 데이터를 바꿀 때 데이터 파일을 곧바로 고치지 않습니다. 메모리에서 먼저 바꾸고, "되돌리는 데 필요한 기록(Undo)"과 "다시 적용하는 데 필요한 기록(Redo)"을 남깁니다.
문서·표준 대조 · MySQL 공식 문서
이 실습에 나오는 개념
- Undo 로그와 Redo 로그
- Undo 로그는 변경 전의 데이터를 기록한 것으로, 트랜잭션을 롤백할 때 쓰이며, InnoDB에서는 다른 트랜잭션에 과거 버전을 보여 줄 때(MVCC)도 쓰인다. Redo 로그는 변경 내용을 기록한 것으로, 커밋된 변경이 데이터 파일에 완전히 쓰이기 전에 장애가 나더라도 재시작 시 이를 다시 적용해 복구하는 데 쓰인다.
- ACID
- 트랜잭션이 보장해야 하는 네 가지 성질이다. 원자성(Atomicity)은 전부 성공하거나 전부 실패하는 것, 일관성(Consistency)은 트랜잭션 전후로 데이터가 정해진 규칙을 만족하는 것, 격리성(Isolation)은 동시에 실행되는 트랜잭션이 서로 간섭하지 않는 것, 지속성(Durability)은 커밋된 결과가 장애가 나도 유지되는 것이다.
- MVCC(다중 버전 동시성 제어)
- 데이터를 변경할 때 이전 버전을 보존해 두어, 읽는 트랜잭션이 자신의 스냅샷 시점에 맞는 버전을 볼 수 있게 하는 방식이다. 스냅샷 시점은 격리 수준에 따라 달라서, MySQL의 REPEATABLE READ는 트랜잭션의 첫 읽기 시점, READ COMMITTED는 문장을 실행할 때마다 새로 잡는다. 읽기 작업이 쓰기 작업을 기다리지 않고 쓰기 작업도 읽기 작업을 기다리지 않아 동시 처리 성능이 높다. MySQL InnoDB, PostgreSQL, Oracle이 모두 이 방식을 쓴다.
- 트랜잭션
- 여러 데이터 변경 작업을 하나의 단위로 묶어, 전부 반영되거나(커밋) 전부 취소되게(롤백) 하는 것이다. 계좌 이체에서 출금과 입금 중 하나만 반영되는 중간 상태를 막는다. 데이터베이스가 정합성을 지키는 가장 기본적인 수단이다.
이렇게 바꿔 보세요
- 처음 설정으로 — 커밋 직후 장애. 데이터 파일에는 옛 값인데 재시작하면 새 값입니다.
- 커밋 전에 장애 — 재시작하면 변경이 없던 일이 됩니다.
- 롤백 — Undo 로그에 남겨 둔 이전 값으로 되돌립니다.
- 정상 커밋 — 커밋 시점에 디스크에 쓰는 것은 로그뿐입니다.
처음 설정으로 실행하면
- 계좌 1의 잔액은 1000입니다. 이 값은 디스크의 데이터 파일에 있고, 메모리(버퍼 풀)에도 같은 값이 올라와 있습니다.
- UPDATE가 시작됩니다. 먼저 바꾸기 전의 값 1000을 Undo 로그에 기록합니다. 되돌려야 할 때와, 다른 트랜잭션이 커밋 전 값을 읽어야 할 때(MVCC) 쓰입니다.
- 메모리에 올라와 있는 데이터를 500으로 바꿉니다. 디스크의 데이터 파일은 여전히 1000입니다. 데이터 파일을 매번 바로 고치면 디스크 이곳저곳에 써야 해서 느리기 때문에, 나중에 모아서 씁니다.
- 무엇을 어떻게 바꿨는지를 Redo 로그에 기록합니다. 로그는 파일 끝에 이어 붙이기만 하면 되어서 데이터 파일을 고치는 것보다 훨씬 빠릅니다.
- COMMIT: Redo 로그를 디스크에 확실히 써 넣습니다(fsync). 이것이 끝나야 사용자에게 "커밋 성공"을 응답합니다. 이 시점에도 데이터 파일은 1000 그대로입니다. 커밋할 때 디스크에 쓰는 것은 로그뿐입니다.
- 커밋 응답 직후, 데이터 파일에 옮겨 쓰기 전에 서버가 꺼졌습니다. 메모리의 500은 사라졌고 데이터 파일에는 1000이 남아 있습니다. 사용자는 이미 "성공"을 받았습니다.
- 서버가 다시 시작됩니다. 복구 과정이 디스크의 Redo 로그를 마지막 체크포인트부터 읽습니다.
- Redo 로그에 "계좌 1을 500으로"와 커밋 표시가 있습니다. 이 변경을 다시 적용합니다. 잔액은 500으로 복구됩니다. 커밋된 데이터는 잃지 않았습니다.
실행 결과
- 커밋된 변경은 데이터 파일에 아직 쓰이지 않았더라도 Redo 로그에서 복구됩니다. "커밋된 것은 장애가 나도 남는다"(지속성)를 Redo 로그가 받쳐 줍니다.
- 이렇게 데이터보다 로그를 먼저 디스크에 쓰는 방식을 WAL(Write-Ahead Logging)이라고 합니다. Undo는 "되돌리기"와 "이전 버전 보여 주기", Redo는 "다시 적용하기"를 맡습니다.
코드
START TRANSACTION;
UPDATE account SET balance = 500 WHERE id = 1; -- 원래 1000
COMMIT;
-- (커밋 응답 직후 서버가 꺼짐)
이 모형의 전제
MySQL InnoDB의 구조를 단순화했습니다. Undo 로그의 변경 자체도 Redo 로그로 보호된다는 점, 로그 버퍼와 여러 단계의 디스크 쓰기 같은 세부는 생략했습니다. 커밋 때마다 Redo 로그를 디스크에 쓰는 것은 기본 설정(innodb_flush_log_at_trx_commit=1)의 동작입니다. 실제 DB에 장애를 일으켜 확인한 결과가 아니라 MySQL 공식 문서의 설명을 옮긴 것입니다.
앞 실습: DB의 교착 상태 · 다음 실습: 버퍼 풀과 페이지