응답이 느린데 top을 봐도 CPU는 한가하고 메모리도 남는다면, 병목이 디스크 IO일 가능성이 큽니다. 프로세스들이 디스크 읽기·쓰기를 기다리며 D(uninterruptible sleep) 상태로 멈춰 있는 것입니다. iostat로 디스크가 실제로 얼마나 바쁜지부터 봅니다.
증상 — IO 대기인지 먼저 확인
top의 %wa(iowait)가 높으면 CPU가 디스크를 기다리고 있다는 신호입니다.
로컬 터미널
top # %Cpu 줄의 wa 값 확인
D 상태 프로세스가 쌓여 있는지도 봅니다.
로컬 터미널
ps -eo state,pid,comm | grep "^D"
진단 — iostat -x로 디스크 부하 보기
sysstat 패키지의 iostat를 확장 모드(-x)로, 1초 간격으로 돌립니다. 첫 출력은 부팅 후 누적 평균이라 무시하고 두 번째 이후 샘플을 봅니다.
로컬 터미널
iostat -x 1
OUTPUT
Device r/s w/s rkB/s wkB/s await r_await w_await aqu-sz %util
nvme0n1 12.0 340.0 480.0 68000.0 24.50 2.10 25.30 8.40 98.7
핵심 컬럼을 이렇게 읽습니다.
| 컬럼 | 의미 | 위험 신호 |
|---|---|---|
%util | 디바이스가 IO 처리에 쓴 시간 비율 | 100%에 근접하면 포화 |
await | 요청당 평균 대기 시간(ms) | HDD 수십 ms·SSD 한 자릿수 초과 시 의심 |
r_await / w_await | 읽기/쓰기 각각의 대기 시간 | 한쪽만 높으면 워크로드 편향 |
aqu-sz | 평균 큐 길이 | 1보다 크게 쌓이면 적체 |
w/s, wkB/s | 초당 쓰기 횟수·양 | 예상보다 크면 폭주하는 쓰기 의심 |
%util이 90%를 넘고 await가 평소보다 크면 디스크가 병목입니다. 단, NVMe처럼 병렬 처리하는 장치는 %util 100%여도 여유가 있을 수 있으니 await·aqu-sz를 함께 봅니다.
원인 좁히기 — 누가 IO를 먹나
디스크가 포화 상태라면 어떤 프로세스가 IO를 일으키는지 iotop으로 잡습니다.
로컬 터미널
iotop -oa # 실제 IO 발생 프로세스만(-o), 누적(-a)
pidstat로도 프로세스별 디스크 IO를 볼 수 있습니다.
로컬 터미널
pidstat -d 1
상위 프로세스를 찾으면 로그 폭주, 백업 작업, DB 체크포인트, 스왑 등 원인을 좁힐 수 있습니다. 메모리 부족으로 스왑이 일어나면 디스크 IO로 나타나니 free -h도 함께 봅니다.
원인별 방향
| 관찰 | 의심 원인 | 점검 |
|---|---|---|
wkB/s 폭주 | 로그·임시파일 대량 쓰기 | 해당 프로세스 로그 설정 |
| 스왑 IO 동반 | 메모리 부족 | free -h, vmstat 1의 si/so |
r_await만 높음 | 캐시 미스·랜덤 읽기 | 쿼리·인덱스, 캐시 크기 |
| 특정 시각 주기적 | cron·백업 | crontab -l, 백업 스케줄 |
체크리스트
로컬 터미널
top # %wa(iowait) 높은가
iostat -x 1 # %util·await 확인(두 번째 샘플부터)
iotop -oa # 어떤 프로세스가 IO 유발
pidstat -d 1 # 프로세스별 IO
free -h && vmstat 1 # 스왑 동반 여부
iostat·vmstat로 성능 병목을 직접 끊어 보는 실습과 리소스 진단 감각은 리눅스 트랙에서 회원가입 없이 무료로 익힐 수 있습니다.