18강 / 20강
DB가 느린 진짜 이유 — fsync 와 WAL
안전하게 저장한다는 것의 비용
이상한 현상이 있습니다. NVMe SSD는 초당 50만 번 쓸 수 있다는데, 쇼핑몰 DB는 초당 수백 건의 주문밖에 못 넣습니다. 1000배 차이가 어디로 갔을까요?
답은 fsync입니다.
"저장했다"는 말의 세 가지 뜻
write()는 커널에 넘겼다는 뜻일 뿐 디스크에 닿았다는 뜻이 아닙니다.
전원이 나가면 사라져요. 그래서 DB는 커밋할 때마다 fsync()를 부릅니다 —
"진짜로 디스크에 박힐 때까지 기다려"라고요.
fsync 한 번의 값이 36번의 그냥 쓰기와 맞먹습니다.
Python
브라우저에서 실행
WAL — 두 번 쓰는데 왜 더 빠른가
DB는 데이터를 고칠 때 먼저 로그에 적고 나서 진짜 데이터를 고칩니다. 이걸 WAL(Write-Ahead Log) 이라고 해요. 두 번 쓰는 셈인데 왜 더 빠를까요?
커밋 시점에는 순차 로그만 fsync 하면 됩니다. 랜덤 쓰기는 나중에 한가할 때 모아서 하고요. 12강에서 봤듯이 순차와 랜덤은 속도가 하늘과 땅 차이라, 두 번 쓰는 게 더 빠릅니다.
게다가 전원이 나가도 로그를 다시 읽어 복구할 수 있어요. 빠르고 안전한, 드문 조합입니다.
디스크 병목을 진단하는 순서
wa 가 20% 넘으면 디스크를 의심합니다.
bash
브라우저에서 실행
%util 만 보지 말고 await 를 함께 봅니다.
bash
브라우저에서 실행
순차는 빠른데 랜덤이 느리면 DB가 고생합니다.
bash
브라우저에서 실행
대응 방법
| 증상 | 대응 |
|---|---|
| 커밋이 많아 fsync가 병목 | 여러 건을 한 트랜잭션으로 묶는다 (그룹 커밋) |
| 같은 데이터를 반복 조회 | 인덱스 · 앱 캐시 · Redis |
| 로그 쓰기가 디스크를 잡아먹음 | 로그를 다른 디스크로, 또는 비동기 기록 |
| 백업이 낮에 돌아 서비스를 방해 | 새벽으로, ionice로 우선순위 낮추기 |
| HDD에서 DB를 돌리는 중 | SSD로 옮긴다. 가장 확실하고 가장 극적인 개선 |
| 디스크가 90% 이상 참 | 정리한다. SSD는 꽉 차면 쓰기가 급격히 느려진다 (12강) |
루프 안에서 매번 commit() 하는 코드는 이 함정에 빠져 있습니다.
Python
브라우저에서 실행
🤔 확인 문제
NVMe SSD가 초당 50만 IOPS인데 DB가 초당 1,000건의 주문밖에 처리하지 못합니다. 가장 큰 이유는?
해설 IOPS 수치는 '큐를 가득 채워 병렬로 던졌을 때'의 값입니다. fsync는 진짜로 디스크에 박힐 때까지 기다리는 동기 작업이라 병렬화가 안 돼요. 그래서 여러 건을 한 트랜잭션으로 묶는 것이 가장 효과적인 대응입니다.
🤔 확인 문제
DB가 데이터를 고칠 때 WAL(로그)에 먼저 쓰고 나중에 데이터 파일을 고치는데, 두 번 쓰는데도 더 빠른 이유는?
해설 순차 쓰기와 랜덤 쓰기의 속도 차이가 워낙 커서(12강), 두 번 쓰더라도 순차로 바꾸는 게 이득입니다. 게다가 전원이 나가도 로그를 재생해 복구할 수 있어요 — 빠르면서 안전해집니다.