장애의 유형과 복구 원리
서버가 갑자기 죽어도 데이터베이스가 다시 일관된 상태로 돌아올 수 있는 이유는 복구(Recovery) 메커니즘 덕분입니다. 로그와 백업 체인이 정상적으로 보존되어 있다면, 커밋된 변경은 가능한 한 재적용하고 커밋되지 않은 변경은 되돌리는 것이 복구의 목표입니다. 복구 시스템은 ACID 속성 중 원자성(Atomicity)과 지속성(Durability)을 보장하는 핵심 메커니즘입니다.
장애의 유형
데이터베이스 시스템에서 발생할 수 있는 장애는 크게 세 가지로 분류됩니다. 각 장애는 발생 빈도, 심각도, 복구 방법이 서로 다릅니다.
| 장애 유형 | 원인 | 빈도 | 심각도 | 복구 방법 |
|---|---|---|---|---|
| 트랜잭션 장애 | 논리 오류, 제약 위반, 데드락 | 빈번 | 낮음 | ROLLBACK (Undo) |
| 시스템 장애 | 정전, OS 크래시, 메모리 오류 | 가끔 | 중간 | 인스턴스 복구 (Redo+Undo) |
| 디스크 장애 | 디스크 손상, 파일 시스템 손상 | 드문 | 높음 | 백업 복원 + 로그 체인 적용 |
트랜잭션 장애
트랜잭션 장애(Transaction Failure)는 개별 트랜잭션이 정상적으로 완료되지 못하는 상황입니다. 가장 빈번하게 발생하지만, 복구도 가장 간단합니다.
트랜잭션 장애의 원인은 다양합니다.
- 논리적 오류: 0으로 나누기, 배열 범위 초과, 잘못된 데이터 입력 등 프로그램 로직의 오류입니다.
- 제약 조건 위반: 기본키 중복, 외래키 참조 위반, CHECK 제약 위반, NOT NULL 위반 등입니다.
- 데드락(Deadlock): 두 트랜잭션이 서로가 점유한 자원을 기다리며 무한 대기에 빠지는 상황입니다. DBMS가 데드락을 감지하면 한쪽 트랜잭션을 강제로 ROLLBACK합니다.
- 타임아웃: 트랜잭션이 지정된 시간 내에 완료되지 못한 경우입니다.
- 사용자 요청: 사용자가 명시적으로 ROLLBACK을 요청한 경우입니다.
-- 제약 조건 위반
BEGIN TRANSACTION;
INSERT INTO students (student_id, name)
VALUES (2024001, '김철수'); -- ✓ 성공
INSERT INTO students (student_id, name)
VALUES (2024001, '이영희'); -- ✗ PK 중복! 트랜잭션 장애
ROLLBACK;
-- Undo 로그를 이용하여 김철수 INSERT도 취소트랜잭션 장애의 복구는 Undo 로그를 이용한 ROLLBACK입니다. 시스템 전체에 영향을 주지 않으며, 해당 트랜잭션만 취소됩니다.
시스템 장애
시스템 장애(System Failure)는 데이터베이스 시스템 전체가 중단되는 상황입니다. 하드웨어 결함이나 소프트웨어 버그, 정전 등으로 인해 발생합니다.
시스템 장애의 핵심적인 특성은 메모리(휘발성 저장소)의 내용이 소실된다는 것입니다. 반면 디스크(비휘발성 저장소)의 데이터는 보존됩니다.
시스템 장애 발생 시, 변경 사항은 로그와 데이터 페이지 반영 여부에 따라 다음처럼 나뉩니다.
- 커밋 완료 + 데이터 페이지 미반영: COMMIT 로그는 안정 저장소에 있지만 데이터 파일에는 아직 반영되지 않은 변경입니다. Redo가 필요합니다.
- 커밋 미완료: 장애 시점에 아직 COMMIT하지 않은 트랜잭션의 변경입니다. 데이터 페이지에 일부 반영되었을 수 있으므로 Undo가 필요합니다.
- 이미 반영된 변경: 페이지 LSN 등을 기준으로 이미 데이터 파일에 반영된 것으로 확인되는 변경입니다. 일반적으로 건너뛰거나 멱등적으로 확인됩니다.
디스크 장애
디스크 장애(Disk Failure)는 데이터 파일이 저장된 디스크가 물리적으로 손상되는 상황입니다. 가장 드물지만 가장 심각한 장애입니다.
디스크 장애에서는 데이터 파일 자체가 소실될 수 있으므로, 현재 온라인 Redo 로그만으로는 충분하지 않을 수 있습니다. 별도 장소에 보관된 백업(Backup)과 아카이브 로그 또는 트랜잭션 로그 백업을 이용하여 백업 시점 이후의 변경을 다시 적용합니다.
디스크 장애를 대비하기 위한 전략은 다음과 같습니다.
- RAID: 디스크 미러링(RAID 1)이나 패리티(RAID 5/6)로 일부 디스크 고장에 대한 가용성을 높입니다. 백업을 대체하지 않으며, 논리 오류나 전체 스토리지 장애까지 해결하지는 못합니다.
- 정기 백업: 전체 백업(Full Backup)과 증분 백업(Incremental Backup)을 주기적으로 수행합니다.
- 아카이브/트랜잭션 로그: Oracle의 ARCHIVELOG 모드, SQL Server의 로그 백업, MySQL의 binary log처럼 백업 이후 변경 이력을 보관해 PITR에 활용합니다.
- 이중화(Replication): 다른 서버에 데이터를 복제하여 장애 시 전환 시간을 줄입니다. 다만 동기/비동기 복제, 자동 failover, split-brain 방지 구성에 따라 RPO와 RTO가 달라집니다.
Redo와 Undo
복구의 두 축은 Redo(재실행)와 Undo(취소)입니다. 이 두 연산은 데이터베이스 복구의 근본 메커니즘입니다.
| 구분 | Redo | Undo |
|---|---|---|
| 목적 | 필요한 변경을 재적용 | 커밋 안 된 변경을 되돌림 |
| 저장 위치 | Redo/WAL 로그 파일 | Undo Segment / Undo 로그 등 |
| 보장 속성 | 지속성 (Durability) | 원자성 (Atomicity) |
| 동작 시점 | 장애 복구 시 롤포워드 | 일반 롤백 또는 장애 복구 |
| 기록 내용 | 재적용에 필요한 변경 정보 | 이전 값 또는 역연산 정보 |
| 방향 | 과거 → 현재 (앞으로) | 현재 → 과거 (뒤로) |
Redo 상세
Redo(재실행)는 장애 후 데이터 페이지에 반영되어야 할 변경 사항을 다시 적용하는 연산입니다. 교육용으로는 "커밋된 변경을 다시 해라"라고 설명하지만, ARIES처럼 역사를 반복하는 알고리즘에서는 커밋 여부와 무관하게 필요한 로그를 재적용한 뒤 미완료 트랜잭션을 Undo할 수 있습니다. Redo를 롤포워드(Roll Forward)라고도 합니다.
Redo 로그에는 변경을 다시 적용하는 데 필요한 정보가 기록됩니다. 교육용으로는 A를 100에서 200으로 변경했다에서 200으로 변경이라는 after image처럼 설명할 수 있지만, 실제 DBMS는 페이지 LSN, 물리/논리 로그 레코드, 보상 로그 등 더 세분화된 구조를 사용합니다.
[LSN: 1001] [TxID: T1] [Table: accounts] [Row: A]
[Operation: UPDATE] [Column: balance]
[After Image: 200]
[Timestamp: 2024-06-15 14:30:01]
[LSN: 1002] [TxID: T1] [COMMIT]
[Timestamp: 2024-06-15 14:30:02]
LSN (Log Sequence Number): 로그 레코드의 순번
TxID: 트랜잭션 식별자
After Image: 변경 후의 값처럼 이해할 수 있는 재적용 정보Undo 상세
Undo(취소)는 커밋되지 않은 트랜잭션의 변경 사항을 이전 상태로 되돌리는 연산입니다. "이 변경은 커밋되지 않았으니, 원래대로 돌려라"입니다. 롤백(Rollback)이라고도 합니다.
Undo 로그에는 변경을 취소하는 데 필요한 이전 값 또는 역연산 정보가 기록됩니다. A를 100에서 200으로 변경했다에서 원래 값은 100이었다라는 before image처럼 이해하면 쉽습니다. 실제 저장 위치와 형식은 DBMS마다 달라서, Oracle/InnoDB처럼 undo 영역을 관리하는 엔진도 있고 로그 레코드와 보상 로그를 조합해 설명하는 모델도 있습니다.
Redo와 Undo의 복구 과정
왜 Redo가 먼저인가
복구 과정에서 Redo를 먼저 수행하고 그 다음에 Undo를 수행하는 이유가 있습니다. 이 순서는 매우 중요합니다.
첫째, 일부 Undo 구조 변경도 Redo 대상이 될 수 있기 때문입니다. DBMS에 따라 undo segment나 rollback 정보의 변경이 로그로 보호되어, Redo 이후에야 미완료 트랜잭션을 되돌릴 충분한 정보가 복원됩니다. 단, 모든 DBMS의 모든 undo 정보가 같은 방식으로 redo-logged 되는 것은 아니므로 구현별 예외가 있습니다.
둘째, 어떤 변경이 디스크에 반영되었는지 로그와 페이지 상태를 기준으로 다시 판단해야 하기 때문입니다. 장애 발생 시점에 어떤 데이터가 Buffer Cache에만 있었고 어떤 데이터가 이미 디스크에 기록되었는지 실행 중 상태만으로는 알 수 없습니다. 복구는 체크포인트와 페이지 LSN을 기준으로 필요한 Redo 범위를 재적용합니다. 이미 반영된 변경은 건너뛰거나 같은 결과가 되도록 처리되므로 멱등성이 중요합니다.
WAL (Write-Ahead Logging)
WAL(Write-Ahead Logging)은 데이터베이스 복구의 기본 원칙입니다. "데이터를 변경하기 전에, 변경 내역을 로그에 먼저 기록한다"는 원칙입니다.
WAL 원칙을 두 가지 규칙으로 정리할 수 있습니다.
규칙 1 (Undo Rule): 데이터 파일에 변경 사항을 기록하기 전에, Undo 정보(Before Image)를 로그에 먼저 기록해야 합니다. 이 규칙이 없으면 변경된 데이터가 디스크에 기록된 후 장애가 발생했을 때, 원래 값을 알 수 없어 롤백이 불가능합니다.
규칙 2 (Redo Rule): 트랜잭션을 커밋하기 전에, 복구에 필요한 Redo 로그 레코드가 안정 저장소에 기록(flush)되어야 합니다. 이 규칙이 없으면 커밋 후 장애가 발생했을 때, 변경 내역을 재적용할 수 없어 지속성이 보장되지 않습니다.
WAL의 성능상 이점은 큽니다. 데이터 파일은 여러 위치에 흩어진 페이지를 갱신해야 해서 랜덤 I/O가 섞이지만, 로그 파일은 대체로 끝에 순차적으로 추가됩니다. 그래서 커밋 시에는 데이터 페이지 전체를 즉시 flush하기보다, 내구성이 필요한 로그를 먼저 안정 저장소에 기록하고 데이터 페이지는 나중에 배치로 기록할 수 있습니다.
체크포인트 (Checkpoint)
체크포인트(Checkpoint)는 복구가 다시 읽기 시작해야 할 로그 위치와 Dirty Page 상태를 줄여 관리하는 기준점입니다. 체크포인트가 진행되면 일부 Dirty Page가 디스크에 기록되고, 복구 시 체크포인트가 기록한 상태를 기준으로 필요한 Redo 범위를 좁히므로 복구 시간이 단축됩니다. 퍼지 체크포인트에서는 실제 redo 시작점이 체크포인트 시각 자체가 아니라 Dirty Page Table의 recLSN처럼 더 이른 로그 위치가 될 수 있습니다.
체크포인트 없이 복구:
DB 시작 시점부터 장애 시점까지의 긴 Redo 범위를 재적용
→ 로그가 몇 GB라면 복구에 매우 오랜 시간 소요
체크포인트 있는 복구:
체크포인트가 가리키는 redo 시작점 이후의 로그를 중심으로 재적용
→ 복구 시간 대폭 단축
시간축:
──────CP1──────CP2──────CP3──────장애──
│
체크포인트 없으면: ◀───────전부 Redo──│
체크포인트 있으면: redo 시작점 이후│
◀──Redo──│체크포인트의 종류는 두 가지입니다.
풀 체크포인트(Full Checkpoint)는 특정 시점까지의 Dirty Page를 가능한 한 디스크에 반영해 복구 시작점을 앞당기는 방식입니다. 구현에 따라 I/O 부담이나 일시적인 지연이 커질 수 있습니다.
퍼지 체크포인트(Fuzzy Checkpoint)는 트랜잭션 처리를 계속하면서 Dirty Page를 점진적으로 기록하고, 복구가 시작해야 할 로그 위치를 관리합니다. 현대 DBMS는 대체로 이런 점진적 체크포인트 계열의 방식을 사용합니다.
로그 기반 복구 알고리즘
실제 DBMS에서 사용하는 대표적인 복구 알고리즘을 살펴보겠습니다.
즉시 갱신 (Immediate Update)
트랜잭션이 활동 중일 때 변경한 페이지가 커밋 전에도 데이터 파일에 내려갈 수 있는 방식입니다. 실제로 매 UPDATE마다 디스크에 즉시 쓰인다는 뜻이라기보다, 버퍼 관리 정책상 미커밋 페이지 flush가 허용된다는 의미입니다. 그래서 커밋 전 장애나 롤백에 대비해 Undo가 필요하고, 커밋된 변경의 지속성을 위해 Redo도 필요합니다.
지연 갱신 (Deferred Update)
교육용 복구 모델에서 지연 갱신은 트랜잭션이 커밋될 때까지 데이터 파일 반영을 미루는 방식입니다. 커밋 전 장애가 발생하면 미커밋 변경이 데이터 파일에 없다고 가정하므로 Undo가 필요 없고, 커밋된 변경을 재적용하는 Redo가 중심이 됩니다. 현대 엔진은 버퍼 관리, WAL, 체크포인트, MVCC 저장 구조를 조합하므로 이 모델은 원리를 분리해서 설명하기 위한 단순화로 보는 것이 좋습니다.
ARIES 알고리즘
ARIES(Algorithm for Recovery and Isolation Exploiting Semantics)는 IBM에서 개발한 대표적인 로그 기반 복구 알고리즘입니다. 여러 상용 DBMS 복구 설계에 큰 영향을 주었지만, PostgreSQL·MySQL·Oracle·SQL Server는 WAL, 체크포인트, Redo/Undo 계열 원리를 각 엔진에 맞게 다르게 구현합니다. ARIES는 세 단계로 복구를 설명합니다.
ARIES의 핵심 원칙은 역사 반복(Repeating History)입니다. Redo 단계에서 필요한 로그 레코드를 커밋 여부와 무관하게 재적용해 장애 직전 상태에 가깝게 복원하고, 그 다음 loser transaction을 보상 로그와 함께 Undo합니다.
복구와 성능의 균형
복구 메커니즘은 데이터의 안전성을 보장하지만, 성능 비용을 수반합니다.
| 메커니즘 | 성능 비용 | 보장하는 것 |
|---|---|---|
| Redo 로그 쓰기 | 변경 작업마다 로그 기록 | 지속성 |
| Undo 로그 쓰기 | 취소 정보 기록 | 원자성 |
| COMMIT 시 flush | 디스크 동기화 대기 | 지속성 |
| 체크포인트 | Dirty Page 일괄 기록 | 복구 시간 단축 |
일부 DBMS는 성능을 위해 비동기 커밋(Asynchronous Commit) 또는 완화된 flush 옵션을 제공합니다. COMMIT 응답 시점과 로그의 안정 저장소 기록 시점을 분리하므로, 장애 시 최근 커밋 일부가 사라질 수 있습니다. PostgreSQL의 synchronous_commit = off, MySQL의 innodb_flush_log_at_trx_commit = 2 같은 설정은 성능과 지속성의 균형을 조정하는 예이며, 복제나 바이너리 로그까지 고려하면 별도 동기화 설정도 함께 봐야 합니다.
백업과 복구 전략
디스크 장애에 대비하기 위한 백업 전략을 상세히 살펴보겠습니다.
백업의 종류
전체 백업(Full Backup)은 데이터베이스의 모든 데이터를 복사하는 방식입니다. 복구 시 전체 백업 하나만 있으면 되므로 복구가 간단하지만, 데이터가 클수록 백업 시간이 오래 걸리고 저장 공간이 많이 필요합니다.
증분 백업(Incremental Backup)은 마지막 백업 이후 변경된 데이터만 복사하는 방식입니다. 백업 시간과 저장 공간이 절약되지만, 복구 시 전체 백업 + 모든 증분 백업을 순차적으로 적용해야 합니다.
차등 백업(Differential Backup)은 마지막 전체 백업 이후 변경된 모든 데이터를 복사하는 방식입니다. 증분 백업보다 백업 크기는 크지만, 복구 시 전체 백업 + 마지막 차등 백업만 있으면 됩니다.
핫 백업과 콜드 백업
콜드 백업(Cold Backup)은 데이터베이스를 중지한 상태에서 수행하는 백업입니다. 데이터 일관성이 보장되지만, 백업 중 서비스를 이용할 수 없습니다. 소규모 시스템이나 정기 점검 시에 사용합니다.
핫 백업(Hot Backup, Online Backup)은 데이터베이스가 운영 중인 상태에서 수행하는 백업입니다. 서비스 중단 없이 백업이 가능하지만, 백업 중에도 데이터가 변경될 수 있으므로 스냅샷, WAL/redo 로그 보존, 백업 시작·종료 지점 기록 같은 일관성 보장 메커니즘이 필요합니다.
PITR (Point-In-Time Recovery)
PITR(Point-In-Time Recovery)은 특정 시점으로 데이터베이스를 복구하는 기술입니다. "어제 오후 3시 상태로 돌려주세요"라는 요청에 대응할 수 있습니다.
DBMS별 백업/복구 도구
| DBMS | 백업 도구 | 설명 |
|---|---|---|
| MySQL | mysqldump | 논리적 백업 (SQL 덤프) |
| MySQL | xtrabackup | 물리적 핫 백업 (InnoDB) |
| PostgreSQL | pg_dump | 논리적 백업 |
| PostgreSQL | pg_basebackup | 물리적 스트리밍 백업 |
| Oracle | RMAN | 통합 백업/복구 관리자 |
| SQL Server | BACKUP DATABASE | 내장 백업 명령 |
-- 논리적 백업 (mysqldump)
-- 모든 데이터를 SQL 문으로 출력
mysqldump -u root -p mydb > backup.sql
-- 복구
mysql -u root -p mydb < backup.sql
-- 바이너리 로그를 이용한 PITR
mysqlbinlog --stop-datetime="2024-06-15 15:00:00" \
binlog.000042 | mysql -u root -p mydb복구 관련 설계 지침
실무에서 장애에 대비한 설계 지침을 정리합니다.
첫째, 백업은 반드시 별도의 물리적 장소에 보관해야 합니다. 같은 서버, 같은 디스크에 백업을 저장하면 디스크 장애 시 백업도 함께 소실됩니다. 클라우드 스토리지나 원격지 서버에 백업을 저장하는 것이 안전합니다.
둘째, 복구 테스트를 정기적으로 수행해야 합니다. 백업이 존재한다고 해서 복구가 반드시 성공하는 것은 아닙니다. 백업 파일이 손상되었거나, 복구 절차에 오류가 있을 수 있습니다. 정기적으로 백업에서 실제 데이터베이스를 복원해보는 DR(Disaster Recovery) 훈련이 필요합니다.
셋째, RPO와 RTO를 비즈니스 요구에 맞게 설정해야 합니다. RPO(Recovery Point Objective)는 "얼마나 최근까지의 데이터를 복구할 수 있는가"이고, RTO(Recovery Time Objective)는 "복구에 얼마나 시간이 걸리는가"입니다.
넷째, PITR이나 미디어 복구가 필요하면 아카이브/트랜잭션 로그 체인을 유지해야 합니다. 이 로그 체인이 없으면 마지막 백업 이후의 변경을 특정 시점까지 재적용할 수 없습니다.
다섯째, 복구 시나리오를 문서화해야 합니다. 장애 유형별로 복구 절차를 사전에 문서화해두면, 실제 장애 발생 시 당황하지 않고 체계적으로 대응할 수 있습니다. "트랜잭션 장애 시", "시스템 장애 시", "디스크 장애 시" 각각에 대한 복구 매뉴얼을 작성하고 정기적으로 업데이트해야 합니다.
장애 유형별 복구 요약
전체 장애 유형과 복구 방법을 한눈에 정리합니다.
데이터베이스의 복구 시스템은 장애 이후에도 일관된 상태로 돌아가도록 설계되었습니다. WAL로 로그를 먼저 기록하고, Redo로 필요한 변경을 재적용하며, Undo로 커밋 안 된 변경을 되돌립니다. 체크포인트로 복구 시간을 줄이고, ARIES 같은 알고리즘으로 복구 절차를 체계화합니다. 정기적인 백업과 복구 테스트는 이 모든 메커니즘의 안전망입니다. 다음 절에서는 Redo 로그의 구조와 WAL(Write-Ahead Logging) 원칙의 구현 세부를 다루겠습니다.