높은 IO wait / 디스크 지연
시스템이 디스크 IO를 기다리고 있다. 먼저 부하가 읽기 중심인지 쓰기 중심인지 구분한 뒤 원인을 좁혀 간다: 부족한 buffer cache, 전체 테이블 스캔, checkpoint storm, IOPS 한도, 임시 공간으로의 spill.
IO는 읽기 중심인가, 쓰기 중심인가?
파일 IO 통계로 대기가 읽기 측(디스크에서 데이터 읽기)인지 쓰기 측(redo/checkpoint/데이터 쓰기)인지 판단한다. 이에 따라 트리가 둘로 나뉜다.
SELECT function_name, small_read_reqs+large_read_reqs AS reads, small_write_reqs+large_write_reqs AS writes,
small_read_megabytes+large_read_megabytes+small_write_megabytes+large_write_megabytes AS mb
FROM v$iostat_function ORDER BY mb DESC;- 주로 읽기 IO
- 주로 쓰기 IO
읽기 IO가 왜 높은가?
과도한 물리 읽기에는 세 가지 주요 원인이 있다: buffer cache가 너무 작음(working set이 RAM에 들어가지 않음), 쿼리가 전체 테이블 스캔을 수행함, 대규모 정렬/해시 작업이 임시 공간으로 spill됨.
SELECT ROUND((1-(pr.value/(dbg.value+cg.value)))*100,2) AS buffer_hit_pct
FROM v$sysstat pr, v$sysstat dbg, v$sysstat cg
WHERE pr.name='physical reads' AND dbg.name='db block gets' AND cg.name='consistent gets';
SELECT sql_id, disk_reads, executions FROM v$sql ORDER BY disk_reads DESC FETCH FIRST 10 ROWS ONLY;- buffer cache 적중률이 낮고 working set이 들어가지 않음
- 쿼리가 전체 테이블 스캔을 수행함
- 대규모 정렬/해시가 임시 공간으로 spill됨
쓰기 IO가 왜 높은가?
쓰기 측 병목은 주기적인 checkpoint/flush가 짧은 시간 동안 디스크를 포화시키는 경우(checkpoint storm)이거나, 스토리지가 부하를 감당할 IOPS/처리량이 부족한 경우다.
SELECT event, total_waits, ROUND(time_waited/100,1) AS sec
FROM v$system_event
WHERE event IN ('log file parallel write','db file parallel write','checkpoint completed')
ORDER BY time_waited DESC;- 갑작스러운 checkpoint/flush 파동(checkpoint storm)
- 스토리지가 지속 부하를 따라가지 못함(IOPS 한도)
근본 원인: buffer cache 부족
자주 접근하는 데이터 집합(working set)이 buffer cache에 들어가지 않아 블록을 디스크에서 계속 다시 읽는다. 적중률은 낮고 물리 읽기는 많다.
근본 원인: 전체 테이블 스캔
적절한 인덱스가 없어 쿼리가 큰 테이블을 처음부터 끝까지 스캔한다. 이로 인해 물리 읽기가 많아지고 쓸모없는 블록이 캐시를 오염시킨다.
근본 원인: 임시 공간으로 spill되는 정렬/해시
대규모 sort, hash join, group by 작업이 작업 메모리에 들어가지 않아 임시 공간(temp/tempdb)에 기록되며 대량의 읽기/쓰기 IO를 발생시킨다.
근본 원인: checkpoint storm
dirty page가 오랫동안 쌓였다가 짧은 간격에 대량으로 디스크에 기록된다. 이 갑작스러운 쓰기 파동이 주기적으로 IO를 포화시킨다.
근본 원인: IOPS / 처리량 부족
스토리지가 워크로드에 필요한 읽기+쓰기 요청을 처리할 IOPS나 대역폭이 부족하다. 큐 깊이와 지연이 지속적으로 높다.