Redo Log 개요: WAL 구조와 crash recovery의 핵심 원리
InnoDB Redo Log가 WAL 원칙으로 commit 내구성과 crash recovery를 보장하는 구조를 운영 관점에서 정리한다.
1. 왜 Redo Log부터 이해해야 하는가
MySQL 운영에서 InnoDB의 redo log는 평소에는 잘 보이지 않지만, 장애가 발생하는 순간 데이터베이스의 신뢰성을 결정한다. 애플리케이션이 COMMIT을 받았다는 것은 단순히 메모리의 행 값이 바뀌었다는 뜻이 아니다. InnoDB가 그 변경을 장애 후에도 다시 재현할 수 있도록 redo record를 안정적인 저장소에 기록했다는 의미가 포함된다. 이 경계가 흐려지면 commit latency, flush 정책, checkpoint 지연, crash recovery 시간, storage 장애 대응을 올바르게 해석하기 어렵다.
Redo log는 변경된 data page 전체를 매번 기록하는 장치가 아니다. InnoDB는 buffer pool의 page를 변경하면서 “어떤 page의 어떤 부분을 어떻게 다시 적용하면 되는지”를 redo record로 남긴다. 장애가 발생하면 InnoDB는 마지막 checkpoint 이후의 redo log를 읽어 committed 변경을 다시 적용하고, 필요하면 미완료 transaction은 undo log를 통해 되돌린다. 이 과정을 이해해야 innodb_flush_log_at_trx_commit, innodb_redo_log_capacity, checkpoint age, recovery time objective를 현실적으로 판단할 수 있다.
운영자가 redo log를 이해해야 하는 대표적인 상황은 다음과 같다.
- 쓰기 부하가 증가하면서 commit latency가 갑자기 커지는 경우
- crash 이후 MySQL이
InnoDB: Starting crash recovery단계에서 오래 머무는 경우 - redo log capacity를 키우면 성능이 좋아진다는 조언을 어디까지 받아들일지 판단해야 하는 경우
- binary log, replication, Group Replication, Aurora storage 계층과 commit 내구성의 관계를 설명해야 하는 경우
innodb_flush_log_at_trx_commit=1을 완화하려는 요구가 있을 때 손실 범위를 명확히 해야 하는 경우
이 글은 redo log를 “파일 몇 개”가 아니라 InnoDB의 WAL(Write-Ahead Logging) 구현으로 보고, 변경 경로와 crash recovery 원리, 운영 지표, 설정 판단 기준을 기술서 관점에서 정리한다.
2. WAL 원칙: data page보다 redo가 먼저다
WAL의 핵심 문장은 짧다. 변경된 data page를 디스크에 쓰기 전에, 그 변경을 복구할 수 있는 log를 먼저 안정적으로 기록한다. InnoDB에서 사용자가 행을 수정하면 실제 경로는 다음처럼 분리된다.
flowchart TD
A[SQL DML 실행] --> B[Buffer Pool의 page latch 획득]
B --> C[Data page 변경]
C --> D[Redo record 생성]
D --> E[Log Buffer에 append]
E --> F{Commit 또는 flush 필요}
F --> G[Redo log file write]
G --> H[fsync 또는 storage flush]
H --> I[COMMIT 반환]
C --> J[Dirty page로 유지]
J --> K[Checkpoint 진행에 따라 tablespace flush]
K --> L[Checkpoint LSN 갱신]
이 흐름에서 중요한 점은 COMMIT과 data page flush가 동일하지 않다는 것이다. InnoDB는 commit 때 변경된 table page를 반드시 즉시 tablespace에 쓰지 않는다. 대부분의 OLTP workload에서 data page는 buffer pool에 dirty page로 남고, page cleaner와 checkpoint에 의해 나중에 기록된다. 대신 commit 내구성은 redo log가 어디까지 기록되었는지로 판단한다.
예를 들어 UPDATE account SET balance = balance - 100 WHERE id = 1이 실행되면 InnoDB는 해당 clustered index page를 buffer pool에서 수정한다. 이 page가 즉시 data file에 내려가지 않아도, redo log에 “이 LSN 이후 이 page에 해당 변경을 적용하라”는 정보가 안정적으로 남아 있으면 crash 후 recovery가 가능하다. 반대로 data page가 먼저 내려가고 그 변경을 설명하는 redo가 남지 않는다면, 장애 후 page 상태와 transaction 상태를 일관되게 재구성하기 어렵다. WAL은 이 순서를 강제하여 복구 가능성을 만든다.
LSN(Log Sequence Number)은 이 구조의 시간축이다. redo record가 생성될 때마다 LSN은 증가한다. checkpoint LSN은 “여기까지의 변경은 data page에도 충분히 반영되었으므로, recovery 때 이보다 오래된 redo를 다시 적용할 필요가 작다”는 경계로 이해할 수 있다. 현재 LSN과 checkpoint LSN의 차이가 커질수록 crash recovery에서 다시 읽고 적용해야 할 redo 범위가 커진다.
3. Redo log 구성 요소: record, log buffer, log file, checkpoint
Redo log를 운영 관점에서 보려면 다음 네 계층을 구분해야 한다.
| 구성 요소 | 역할 | 운영자가 보는 증상 |
|---|---|---|
| redo record | page 변경을 재현할 수 있는 물리적 변경 정보 | DML 양과 index 수가 많을수록 생성량 증가 |
| log buffer | redo record가 파일로 내려가기 전 모이는 메모리 영역 | 큰 transaction, 높은 commit rate에서 flush 압력 증가 |
| redo log file/capacity | redo record가 순환 구조로 저장되는 디스크 영역 | capacity 부족 시 checkpoint 압박과 write stall 가능 |
| checkpoint | dirty page flush가 redo 재사용 경계를 앞으로 미는 과정 | checkpoint age 증가, recovery 시간 증가, page cleaner 부하 |
Redo record는 SQL 문장 자체가 아니다. InnoDB crash recovery는 UPDATE SQL을 다시 실행하는 방식으로 복구하지 않는다. SQL을 재실행하면 trigger, nondeterministic function, concurrent transaction 순서 같은 문제가 발생할 수 있다. 대신 InnoDB는 page 단위의 물리적 변경 정보를 기록하고, recovery 때 page LSN을 기준으로 필요한 redo만 다시 적용한다. 이것이 redo log가 statement log나 binary log와 다른 지점이다.
Log buffer는 redo record를 잠시 모으는 메모리 공간이다. 사용자가 commit하면 InnoDB는 flush 정책에 따라 log buffer의 내용을 redo log file에 write하고, 필요하면 storage flush까지 수행한다. innodb_log_buffer_size가 너무 작으면 큰 transaction이 commit 전에 log buffer 공간 확보를 위해 중간 flush를 유발할 수 있다. 하지만 짧은 transaction에서 병목이 매 commit의 fsync라면 log buffer 크기만 키워도 해결되지 않는다.
Redo log file은 MySQL 8.0 계열에서 innodb_redo_log_capacity 중심으로 관리한다. 오래된 버전의 innodb_log_file_size, innodb_log_files_in_group 조합을 기억하고 있는 운영자는 버전별 차이를 확인해야 한다. Capacity는 단순히 “클수록 좋은 값”이 아니다. 너무 작으면 checkpoint가 자주 밀려 쓰기 stall 위험이 커지고, 너무 크면 장애 후 recovery에서 처리할 수 있는 redo 범위가 커져 복구 시간이 늘어날 수 있다. 실제 적정값은 redo 생성량, dirty page flush 처리량, RTO, storage 성능을 함께 보고 결정한다.
Checkpoint는 redo log 재사용을 가능하게 하는 안전 경계다. InnoDB redo log는 무한히 증가하는 append-only 파일이 아니라 순환 구조에 가깝게 동작한다. 이미 data page에 반영되어 recovery에 필요하지 않은 오래된 redo 영역은 재사용할 수 있다. 그러나 dirty page flush가 느려 checkpoint가 앞으로 나아가지 못하면 redo 공간이 부족해지고, 결국 사용자 thread가 page flush 또는 log 공간 확보를 기다리는 상황이 생긴다.
4. Commit 경로와 내구성 경계
사용자가 COMMIT을 실행할 때 InnoDB가 보장하는 수준은 설정에 따라 달라진다. 특히 innodb_flush_log_at_trx_commit은 redo log write와 fsync의 경계에 직접 영향을 준다.
| 값 | commit 시 redo 처리 | 장애 시 해석 | 운영 판단 |
|---|---|---|---|
1 |
매 commit마다 redo log write와 flush를 수행 | 정상적인 storage 보장을 전제로 commit 손실 가능성이 가장 낮음 | 일반 OLTP 기본값 |
2 |
매 commit마다 write하지만 flush는 주기적 수행 | OS crash, 전원 장애, storage cache 손실 시 최근 commit 손실 가능 | 손실 허용 범위가 명확한 경우만 검토 |
0 |
commit마다 write/flush를 강제하지 않음 | mysqld crash만으로도 최근 commit 손실 가능 | 일반적인 영속 데이터에는 부적합 |
여기서 write와 flush를 혼동하면 안 된다. write는 운영체제에 파일 쓰기를 요청하는 단계이며, 데이터가 OS page cache나 storage cache에 남아 있을 수 있다. flush 또는 fsync는 더 강한 내구성 경계다. 물론 실제 물리 보장은 filesystem, cloud block storage, controller cache, write barrier, 가상화 계층에 따라 달라질 수 있다. 그러나 MySQL 설정의 의미를 해석할 때는 최소한 이 두 단계를 분리해야 한다.
Binary log가 활성화된 환경에서는 sync_binlog도 함께 봐야 한다. InnoDB redo log가 안전해도 binary log가 손실되면 crash 후 replication consistency나 point-in-time recovery 경로가 꼬일 수 있다. 반대로 binary log만 안전하고 InnoDB redo가 안전하지 않아도 로컬 data file과 transaction 상태가 일치하지 않을 수 있다. 내구성이 중요한 OLTP에서는 보통 innodb_flush_log_at_trx_commit=1, sync_binlog=1 조합을 기준으로 검토하고, 성능을 위해 완화할 때는 장애 시나리오별 손실 범위를 문서화해야 한다.
Aurora MySQL은 storage 계층이 Community MySQL과 다르다. Aurora는 분산 storage volume에 log record를 전파하고 quorum 기반으로 쓰기 내구성을 확보하는 구조를 사용한다. 따라서 로컬 EBS 또는 로컬 파일 시스템에 redo log file을 fsync하는 전통적 그림과 내부 구현은 다르다. 그러나 운영자가 구분해야 할 원칙은 동일하다. commit이 언제 durable하다고 볼 수 있는지, writer failover 후 어느 지점까지 재생 가능한지, Performance Insights와 CloudWatch에서 commit latency와 storage write latency를 어떻게 볼 것인지가 핵심이다.
5. Crash recovery는 무엇을 다시 하는가
MySQL이 비정상 종료된 뒤 InnoDB가 시작되면 crash recovery가 실행된다. 이 과정은 대략 다음 단계로 이해할 수 있다.
sequenceDiagram
participant S as mysqld startup
participant C as Checkpoint LSN
participant R as Redo Log
participant P as Data Page
participant U as Undo Log
S->>C: 마지막 checkpoint 위치 확인
S->>R: checkpoint 이후 redo scan
R->>P: page LSN이 낮은 page에 redo 적용
S->>U: 미완료 transaction 확인
U->>P: committed 되지 않은 변경 rollback
S->>S: 일관된 상태로 서비스 시작
Redo 적용 단계에서는 checkpoint 이후 redo record를 읽고 필요한 page에 다시 적용한다. 이때 page에는 자체 LSN이 있으므로 이미 해당 변경이 반영된 page에는 같은 redo를 중복 적용하지 않는다. 이 성질 때문에 recovery는 crash 직전 dirty page가 일부만 data file에 내려간 상태에서도 일관성을 회복할 수 있다.
Undo는 다른 목적을 갖는다. Redo는 committed 변경을 포함하여 page 상태를 앞으로 재현하는 데 필요하고, undo는 commit되지 않은 transaction을 논리적으로 되돌리는 데 필요하다. 장애 시점에 어떤 transaction은 data page에 일부 변경을 남겼지만 commit하지 못했을 수 있다. InnoDB는 redo로 page를 crash 직전의 물리 상태에 가깝게 복구한 뒤, undo를 통해 미완료 transaction의 효과를 제거한다.
Recovery 시간이 길어지는 대표 원인은 checkpoint 이후 redo 범위가 크거나, dirty page flush가 충분히 진행되지 않았거나, storage read/write latency가 높은 경우다. Redo log capacity를 크게 잡으면 checkpoint 압박은 줄어들 수 있지만 recovery가 읽어야 할 잠재 범위도 커질 수 있다. 따라서 “큰 redo log는 성능에 좋다”는 말은 절반만 맞다. 쓰기 burst 흡수에는 도움이 될 수 있지만, 장애 복구 시간 목표가 엄격한 시스템에서는 capacity, checkpoint 진행, flush 처리량을 함께 검증해야 한다.
6. 운영 진단: 현재 설정과 redo 관련 계측 확인
다음 SQL은 MySQL 8.0 이상에서 현재 redo log와 commit 내구성 관련 설정을 확인하는 기본 예다. 운영 환경에서는 결과를 정기적으로 수집하여 설정 변경 이력과 함께 보관하는 것이 좋다.
SELECT VERSION() AS mysql_version;
SHOW VARIABLES
WHERE Variable_name IN (
'innodb_flush_log_at_trx_commit',
'innodb_redo_log_capacity',
'innodb_log_buffer_size',
'sync_binlog'
);
실행 결과(MySQL 8.0.46):
mysql> SELECT VERSION() AS mysql_version;
+---------------+
| mysql_version |
+---------------+
| 8.0.46 |
+---------------+
1 row in set (0.00 sec)
mysql> SHOW VARIABLES
-> WHERE Variable_name IN (
-> 'innodb_flush_log_at_trx_commit',
-> 'innodb_redo_log_capacity',
-> 'innodb_log_buffer_size',
-> 'sync_binlog'
-> );
+--------------------------------+-----------+
| Variable_name | Value |
+--------------------------------+-----------+
| innodb_flush_log_at_trx_commit | 1 |
| innodb_log_buffer_size | 16777216 |
| innodb_redo_log_capacity | 104857600 |
| sync_binlog | 1 |
+--------------------------------+-----------+
4 rows in set (0.01 sec)
위 결과에서 innodb_flush_log_at_trx_commit과 sync_binlog는 commit 내구성 정책을 해석하는 핵심 값이다. innodb_redo_log_capacity는 checkpoint 압박과 recovery 범위에 영향을 주며, innodb_log_buffer_size는 큰 transaction 또는 redo burst에서 중간 flush 가능성과 관련된다.
InnoDB 내부 metric은 버전과 설정에 따라 표시 상태가 다를 수 있다. 다음 쿼리는 information_schema.INNODB_METRICS에서 redo와 checkpoint 관련 항목 일부를 확인하는 예다. 운영 환경에서 이 값들은 절대값 하나보다 시간에 따른 증가율과 부하 상황의 상관관계로 해석해야 한다.
SELECT NAME,
SUBSYSTEM,
COUNT,
STATUS
FROM information_schema.INNODB_METRICS
WHERE NAME IN (
'log_lsn_current',
'log_lsn_checkpoint_age',
'log_lsn_last_checkpoint',
'log_writes',
'log_waits'
)
ORDER BY NAME;
실행 결과(MySQL 8.0.46):
mysql> SELECT NAME,
-> SUBSYSTEM,
-> COUNT,
-> STATUS
-> FROM information_schema.INNODB_METRICS
-> WHERE NAME IN (
-> 'log_lsn_current',
-> 'log_lsn_checkpoint_age',
-> 'log_lsn_last_checkpoint',
-> 'log_writes',
-> 'log_waits'
-> )
-> ORDER BY NAME;
+-------------------------+-----------+-------+----------+
| NAME | SUBSYSTEM | COUNT | STATUS |
+-------------------------+-----------+-------+----------+
| log_lsn_checkpoint_age | log | 0 | disabled |
| log_lsn_current | log | 0 | disabled |
| log_lsn_last_checkpoint | log | 0 | disabled |
| log_waits | log | 0 | enabled |
| log_writes | log | 26 | enabled |
+-------------------------+-----------+-------+----------+
5 rows in set (0.00 sec)
log_waits가 증가한다면 log buffer 공간 또는 log write 경로에서 대기가 발생했을 가능성을 의심할 수 있다. 다만 이 지표 하나로 원인을 단정해서는 안 된다. 동시에 commit latency, storage latency, dirty page 비율, checkpoint age, CPU 사용률, fsync 관련 대기 이벤트를 함께 봐야 한다.
Performance Schema의 wait event를 함께 확인하면 redo log 관련 file I/O 대기 관측 가능성을 판단할 수 있다. 다음 쿼리는 redo log file I/O 계측이 현재 활성화되어 있는지 확인하는 예다.
SELECT NAME,
ENABLED,
TIMED
FROM performance_schema.setup_instruments
WHERE NAME LIKE 'wait/io/file/innodb/innodb_log_file%';
실행 결과(MySQL 8.0.46):
mysql> SELECT NAME,
-> ENABLED,
-> TIMED
-> FROM performance_schema.setup_instruments
-> WHERE NAME LIKE 'wait/io/file/innodb/innodb_log_file%';
+-------------------------------------+---------+-------+
| NAME | ENABLED | TIMED |
+-------------------------------------+---------+-------+
| wait/io/file/innodb/innodb_log_file | YES | YES |
+-------------------------------------+---------+-------+
1 row in set (0.00 sec)
이 쿼리가 빈 결과를 반환하거나 계측명이 버전에 따라 다르면, 해당 MySQL minor version의 Performance Schema instrument 이름을 확인해야 한다. 운영 문서에서는 특정 지표명을 암기하기보다 “redo log write/fsync 경로가 계측되고 있는지”를 확인하는 절차를 표준화하는 편이 안전하다.
7. 작은 재현 예제로 보는 redo 발생
Redo log는 내부 구조이므로 SQL만으로 “redo record 내용”을 직접 읽는 것은 일반 운영 절차가 아니다. 그러나 DML이 redo 관련 metric 증가와 연결된다는 점은 작은 테이블 변경으로 확인할 수 있다. 아래 예제는 테스트 환경에서 metric 값을 전후로 비교하는 구조다. 결과 값은 MySQL 버전, metric 활성화 상태, background activity에 따라 달라질 수 있으므로 절대값이 아니라 변화 방향을 본다.
DROP TABLE IF EXISTS redo_note_demo;
CREATE TABLE redo_note_demo (
id BIGINT PRIMARY KEY,
payload VARCHAR(100) NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB;
SELECT NAME, COUNT
FROM information_schema.INNODB_METRICS
WHERE NAME IN ('log_lsn_current', 'log_writes')
ORDER BY NAME;
INSERT INTO redo_note_demo (id, payload)
VALUES (1, 'redo log test'),
(2, 'write ahead logging');
UPDATE redo_note_demo
SET payload = CONCAT(payload, ' updated')
WHERE id = 1;
SELECT id, payload
FROM redo_note_demo
ORDER BY id;
SELECT NAME, COUNT
FROM information_schema.INNODB_METRICS
WHERE NAME IN ('log_lsn_current', 'log_writes')
ORDER BY NAME;
DROP TABLE redo_note_demo;
실행 결과(MySQL 8.0.46):
mysql> DROP TABLE IF EXISTS redo_note_demo;
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE redo_note_demo (
-> id BIGINT PRIMARY KEY,
-> payload VARCHAR(100) NOT NULL,
-> updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
-> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT NAME, COUNT
-> FROM information_schema.INNODB_METRICS
-> WHERE NAME IN ('log_lsn_current', 'log_writes')
-> ORDER BY NAME;
+-----------------+-------+
| NAME | COUNT |
+-----------------+-------+
| log_lsn_current | 0 |
| log_writes | 38 |
+-----------------+-------+
2 rows in set (0.00 sec)
mysql> INSERT INTO redo_note_demo (id, payload)
-> VALUES (1, 'redo log test'),
-> (2, 'write ahead logging');
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> UPDATE redo_note_demo
-> SET payload = CONCAT(payload, ' updated')
-> WHERE id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> SELECT id, payload
-> FROM redo_note_demo
-> ORDER BY id;
+----+-----------------------+
| id | payload |
+----+-----------------------+
| 1 | redo log test updated |
| 2 | write ahead logging |
+----+-----------------------+
2 rows in set (0.00 sec)
mysql> SELECT NAME, COUNT
-> FROM information_schema.INNODB_METRICS
-> WHERE NAME IN ('log_lsn_current', 'log_writes')
-> ORDER BY NAME;
+-----------------+-------+
| NAME | COUNT |
+-----------------+-------+
| log_lsn_current | 0 |
| log_writes | 43 |
+-----------------+-------+
2 rows in set (0.00 sec)
mysql> DROP TABLE redo_note_demo;
Query OK, 0 rows affected (0.01 sec)
운영 환경에서 이와 같은 실험을 그대로 수행해서는 안 된다. 실험은 별도 테스트 인스턴스에서 하고, 운영에서는 기존 metric과 workload 지표를 관찰해야 한다. 특히 큰 transaction을 일부러 발생시키거나 redo capacity 한계까지 밀어붙이는 테스트는 복구 시간과 write stall을 유발할 수 있으므로 운영 인스턴스에서 수행하지 않는다.
8. Redo log capacity와 checkpoint age 판단
Redo log capacity를 조정할 때는 단순히 현재 파일 크기만 보지 말고 세 가지 질문을 함께 해야 한다.
-
정상 부하에서 redo 생성 속도는 얼마인가?
초당 변경량이 큰 workload는 redo를 빠르게 생성한다. Secondary index가 많거나 큰 row를 자주 수정하면 redo 생성량도 커진다. -
Dirty page flush가 redo 생성 속도를 따라가는가?
Checkpoint가 앞으로 움직이려면 관련 dirty page가 tablespace에 기록되어야 한다. Storage write 성능이 낮거나 buffer pool dirty page가 많이 쌓이면 checkpoint age가 커진다. -
장애 후 허용 가능한 recovery 시간은 얼마인가?
Capacity가 크면 burst를 흡수할 수 있지만 crash recovery가 처리할 수 있는 redo 범위도 커질 수 있다. RTO가 엄격한 서비스에서는 장애 훈련으로 실제 recovery 시간을 측정해야 한다.
다음과 같은 증상이 동시에 나타나면 redo/checkpoint 경로를 점검해야 한다.
- 쓰기 peak 시간대에 commit latency가 증가한다.
- InnoDB 관련 log wait 또는 fsync 대기가 증가한다.
- Dirty page 비율이 높고 page cleaner가 계속 바쁘다.
- MySQL 재시작 후 crash recovery 시간이 과거보다 길어진다.
- Storage write latency가 증가하면서 application timeout이 함께 발생한다.
Capacity를 키우는 것은 한 가지 대응일 뿐이다. Storage 성능 개선, transaction 크기 축소, batch commit 설계, secondary index 정리, hot row 갱신 완화, checkpoint 관련 설정 검토가 함께 필요할 수 있다. 특히 “capacity를 키우면 stall이 사라진다”는 식의 단정은 위험하다. Dirty page flush 처리량이 근본적으로 부족하면 더 큰 capacity는 문제를 뒤로 미룰 뿐이다.
9. Binary log, replication, backup과의 관계
Redo log는 InnoDB storage engine 내부의 crash recovery 장치다. Binary log는 server layer의 변경 이벤트 기록이며 replication과 point-in-time recovery에 사용된다. 두 log는 목적과 형식이 다르다.
| 항목 | Redo log | Binary log |
|---|---|---|
| 담당 계층 | InnoDB storage engine | MySQL server layer |
| 주요 목적 | crash recovery | replication, PITR, 감사성 변경 추적 |
| 기록 내용 | page 변경을 재현하는 물리적 redo record | statement 또는 row event |
| 운영 변수 | innodb_flush_log_at_trx_commit, innodb_redo_log_capacity |
sync_binlog, binlog_format, 보존 정책 |
| 직접 복구 방식 | InnoDB startup 시 자동 적용 | mysqlbinlog 등으로 재적용 |
복제 환경에서는 source에서 commit된 transaction이 redo와 binary log 양쪽에서 일관되게 durable해야 한다. MySQL은 내부적으로 XA-like coordination을 통해 InnoDB transaction과 binary log commit 순서를 맞춘다. 그러나 설정이 완화되어 redo 또는 binlog flush가 지연되면 OS crash 같은 장애에서 양쪽의 영속성 경계가 달라질 수 있다. 운영자는 replication lag뿐 아니라 장애 후 source 재시작, replica 재동기화, PITR 가능 지점을 함께 고려해야 한다.
Backup과도 관계가 있다. Physical backup은 data file과 redo/undo 정보를 사용해 일관된 시점의 복구 가능 상태를 만든다. Backup 도중 data page가 계속 바뀌더라도 redo 정보를 함께 확보하면 prepare 단계에서 일관성을 맞출 수 있다. Aurora에서는 snapshot과 continuous backup이 storage 계층에서 관리되지만, 역시 log 기반 복구 원리는 개념적으로 중요하다. 사용자는 “snapshot이 있다”는 사실만이 아니라 특정 시점으로 복구할 때 어떤 log 범위가 필요한지 이해해야 한다.
10. 흔한 오해와 장애 모드
오해 1: commit되면 data file에 즉시 쓰인다
InnoDB의 일반적인 commit은 data page 즉시 flush가 아니라 redo log 내구성에 기반한다. Data file은 나중에 checkpoint에 따라 갱신될 수 있다. 이 구조 덕분에 OLTP 성능이 가능하지만, crash recovery가 필요한 이유도 여기서 나온다.
오해 2: redo log capacity는 클수록 항상 좋다
큰 capacity는 checkpoint 압박을 완화할 수 있지만 recovery 시간을 늘릴 수 있다. 또한 dirty page flush가 계속 뒤처지는 환경에서는 capacity 증설이 근본 해결이 아니다. Capacity 조정은 redo 생성량, checkpoint age, RTO를 함께 보고 결정해야 한다.
오해 3: innodb_flush_log_at_trx_commit=2는 거의 안전하다
2는 mysqld 프로세스 crash에는 비교적 강할 수 있지만 OS crash, 전원 장애, storage cache 손실에서는 최근 commit이 손실될 수 있다. “거의 안전”이라는 표현보다 “어떤 장애 범위에서 얼마만큼 손실될 수 있는가”로 설명해야 한다.
오해 4: redo log와 binary log는 같은 장애를 해결한다
Redo log는 InnoDB crash recovery를 위한 것이고, binary log는 replication과 PITR을 위한 것이다. 하나가 있다고 다른 하나의 역할이 대체되지 않는다. 특히 PITR 전략을 세울 때 redo log 파일을 오래 보관한다고 binary log 보존을 대신할 수 없다.
장애 모드: checkpoint가 밀린 상태의 crash
쓰기 burst 동안 dirty page가 많이 쌓이고 checkpoint age가 커진 상태에서 crash가 발생하면, startup recovery가 길어질 수 있다. 이때 운영자는 단순히 mysqld가 느리다고 판단하기보다 redo scan과 apply가 진행 중인지 error log를 확인해야 한다. 무리하게 프로세스를 반복 재시작하면 recovery가 더 지연될 수 있다.
장애 모드: storage flush latency 증가
Storage latency가 증가하면 commit latency가 같이 증가할 수 있다. innodb_flush_log_at_trx_commit=1에서는 매 commit 또는 group commit 경로에서 flush 비용이 민감하게 반영된다. 이 상황에서 무작정 durability 설정을 완화하면 장애 시 데이터 손실 위험을 성능 문제의 임시 해법으로 떠넘기는 결과가 될 수 있다. 먼저 storage 지표, fsync 대기, batch commit 가능성, transaction 경계를 검토해야 한다.
11. 운영 점검표
Redo log와 WAL 구조를 운영 정책으로 연결할 때는 다음 항목을 정기적으로 점검한다.
-
innodb_flush_log_at_trx_commit과sync_binlog
12. 결론: Redo Log는 InnoDB 내구성의 시간축이다
Redo log는 InnoDB가 빠른 OLTP 성능과 crash recovery를 동시에 달성하기 위해 사용하는 핵심 구조다. Data page를 매 commit마다 즉시 기록하지 않으면서도 장애 후 committed 변경을 복구할 수 있는 이유는 WAL 원칙, redo record, LSN, checkpoint가 함께 동작하기 때문이다.
운영자는 redo log를 설정 파일의 숫자로만 보지 말아야 한다. COMMIT이 반환되는 내구성 경계, storage flush 비용, checkpoint 진행, recovery 시간, binary log와의 일관성까지 연결해 보아야 한다. 다음 단계의 InnoDB 내부 학습에서는 undo log, MVCC, checkpoint, doublewrite buffer가 redo log와 어떻게 맞물려 일관성과 성능을 구성하는지 더 세밀하게 다룰 수 있다.