MySQL InnoDB 엔진의 Redo Log가 당신의 시작을 구했을 때
Source: Dev.to
등대지기 비유
InnoDB의 redo log를 등대지기의 일지라고 생각해 보세요.
현대 GPS가 나오기 전, 등대지기들은 꼼꼼히 일지를 기록했습니다:
- “오후 3시 15분 — 북쪽으로 선박이 지나감.”
- “오후 4시 22분 — 안개 때문에 등대 점등.”
- “오후 5시 03분 — 바람이 서쪽으로 전환.”
뭔가 문제가 생기면 조사관들은 일지를 읽어 무슨 일이 있었는지 재구성할 수 있었습니다.
InnoDB도 같은 방식으로 동작합니다. 어떤 작업을 바꾸기 전에 먼저 redo log에 기록합니다:
“테이블 users의 행 47을 업데이트하려고 합니다. 여기서 바뀌는 내용은 …”
이 write‑ahead logging (WAL) 은 크래시, 전원 장애, 그리고 RAM의 비트를 뒤집는 우주선까지도 대비하는 안전망입니다.
Note: 등대지기가 병목이 될 수 있습니다.
우리를 거의 죽음에 이르게 한 트랜잭션 패턴
우리 애플리케이션은 금융 트랜잭션을 빠르게 처리했습니다—피크 시 초당 ~5,000 건의 쓰기 작업을 수행했습니다.
전형적인 트랜잭션은 다음과 같았습니다:
-- 초당 수천 번 실행
START TRANSACTION;
UPDATE accounts
SET balance = balance - 100
WHERE user_id = ?;
INSERT INTO transaction_log
(user_id, amount, timestamp)
VALUES (?, 100, NOW());
UPDATE user_metadata
SET last_transaction = NOW()
WHERE user_id = ?;
COMMIT;
겉보기엔 무해해 보이죠? 틀렸습니다. 이 패턴이 우리 redo log 시스템을 조용히 질식시키고 있었습니다.
Redo Log: 기술적 깊이 탐구 (지루한 부분 제외)
InnoDB 내부에서 일어나는 일을 시각화하면:
┌─────────────────────────────────────────────────────┐
│ Transaction Flow │
├─────────────────────────────────────────────────────┤
│ 1. Client: "UPDATE accounts..." │
│ │ │
│ ▼ │
│ 2. InnoDB: Write to REDO LOG (on disk) │
│ [Sequential write - fast! ⚡] │
│ │ │
│ ▼ │
│ 3. InnoDB: Update buffer pool (in RAM) │
│ [Random access - still fast! 💨] │
│ │ │
│ ▼ │
│ 4. Client: "COMMIT" │
│ │ │
│ ▼ │
│ 5. InnoDB: Flush redo to disk (fsync) │
│ [This is where we wait... 🐌] │
└─────────────────────────────────────────────────────┘
핵심 인사이트: Redo 쓰기는 순차적(빠름)인데 fsync는 매우 느림—특히 부하가 클 때. 초당 5,000 건의 트랜잭션마다 디스크 플러시가 필요했으니, 저장소에 초당 5,000 개의 동기식 작업을 요구한 셈이며, 이는 SSD조차 싫어하는 상황이었습니다.
조사 결과: 모든 것을 바꾼 네 가지 변수
1. innodb_flush_log_at_trx_commit
-- 가장 안전하지만 가장 느림
SET GLOBAL innodb_flush_log_at_trx_commit = 1;
-- OS 캐시 사용, 더 빠름
SET GLOBAL innodb_flush_log_at_trx_commit = 2;
-- 위험하지만 가장 빠름
SET GLOBAL innodb_flush_log_at_trx_commit = 0;
| 설정 | 내구성 | 성능 | 사용 사례 |
|---|---|---|---|
| 1 | 완전 ACID | 가장 느림 | 금융 데이터 |
| 2 | OS 캐시 | 빠름 | 웹 애플리케이션 |
| 0 | 위험 | 가장 빠름 | 분석 작업 |
우리는 2 로 전환했고, 10배의 처리량 향상을 얻었습니다.
2. innodb_log_file_size
redo log는 원형 버퍼입니다:
[Checkpoint] ---> ███████ write position ---> (wrap)
쓰기 포인터가 체크포인트에 도달하면 정체가 발생합니다.
SHOW VARIABLES LIKE 'innodb_log_file_size';
우리는 파일 크기를 48 MB에서 2 GB × 2 파일 = 4 GB 로 늘렸습니다.
경험 법칙: 피크 쓰기를 ≈ 1시간 정도 버틸 수 있을 만큼 크게 잡으세요.
3. innodb_flush_method
SET GLOBAL innodb_flush_method = 'O_DIRECT';
이 설정은 이중 캐싱을 방지하고 I/O 대기 시간을 30 % 줄였습니다.
4. 커밋을 배치하세요
이전:
5000 commits/sec → 5000 fsync calls
배치 후:
100 commits/sec → 100 fsync calls
결과: 디스크 작업이 50배 감소했습니다.
읽기 전용 트랜잭션 비밀 무기
START TRANSACTION READ ONLY;
SELECT SUM(balance)
FROM accounts
WHERE region = 'US-WEST';
COMMIT;
InnoDB는 이후:
- redo 로그 기록을 건너뜀
- 락을 건너뜀
- 트랜잭션 ID 생성을 건너뜀
결과: 보고 쿼리에서 ≈ 4배 빠르게 동작합니다.
Redo Log 모니터링
SELECT
'Redo Log Usage' AS metric,
ROUND(
(SELECT VARIABLE_VALUE
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_os_log_written') / 1024 / 1024,
2
) AS value
UNION ALL
SELECT
'Log Waits',
(SELECT VARIABLE_VALUE
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_log_waits')
UNION ALL
SELECT
'Checkpoint Age',
ROUND(
(SELECT VARIABLE_VALUE
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_checkpoint_age') / 1024 / 1024,
2
);
Innodb_log_waits > 0이면 → redo log가 너무 작다는 의미입니다.
프로덕션 최적화 플레이북
-
최적화 전 프로파일링
SET GLOBAL slow_query_log = 1; SET GLOBAL long_query_time = 0.1; SET GLOBAL log_slow_extra = 1; -
Redo Log 적정 크기 설정
일일 메트릭을 활용해 피크 시간의 ≈ 2배 크기로 로그 파일을 잡으세요. -
벤치마크로 내구성 설정 테스트
innodb_flush_log_at_trx_commit값을 1, 2, 0 으로 비교합니다. -
보고 및 분석에는 읽기 전용 트랜잭션 사용.
교훈
- redo log는 안전망이자 병목입니다.
- 모든 내구성 보장은 성능 비용을 동반합니다.
- 로그 파일 크기 조정은 미션 크리티컬합니다.
- 읽기 전용 트랜잭션은 크게 활용되지 않고 있습니다.
- 쓰기 배치는 대규모 환경에서 가장 강력한 최적화 중 하나입니다.
추가 읽을거리
- MySQL InnoDB Documentation: Redo Log
- Percona: Tuning InnoDB Redo Logging
- High Performance MySQL (O’Reilly)
Tags: #mysql #database #performance #devops