[DBMS] 디스크I/O (DISK I/O) 란?

1.디스크 I/O(Disk Input/Output)란?

데이터베이스가 디스크(HDD/SSD)에서 데이터를 읽거나 쓰는 작업

 

2. 디스크 I/O가 중요한 이유?

DB는 기본적으로 데이터를 디스크에 저장한다.

하지만 CPU나 메모리에 비해 디스크 접근 속도가 훨씬 느리기 때문에, 디스크 I/O가 많아질수록 쿼리가 느려진다.

(메모리 I/O는 전기적 신호인 데 반해, 디스크 I/O는 액세스 암을 통해 물리적 작용이 동반되므로 메모리I/O에 비해 약 10,000배쯤 느리다)

따라서 SQL 튜닝에 있어서 디스크 I/O는 핵심 지표 중 하나이다.

 

또한 프로세스는 디스크 I/O 발생 시 대기 상태(Wait/Sleep) 로 전환되고, OS 스케줄러가 CPU를 다른 프로세스에 할당 후 I/O가 완료되면 다시 실행 가능 상태로 돌아오는 생명주기를 가지고 있기 때문에, 컨텍스트 스위칭과 대기 시간이 누적될수록 전체 성능이 저하된다. (동기 I/O, Blocking I/O 기준. 비동기(Async) I/O나 Non-blocking I/O 방식에서는 프로세스가 대기하지 않고 계속 다른 작업을 할 수도 있음)

 

※ 대략적인 속도 비교

  • CPU 캐시: 나노초(ns) 단위
  • 메모리(RAM): 수십~수백 ns
  • SSD: 수십~수백 마이크로초(μs)
  • HDD: 수 밀리초(ms) → SSD 대비 수백~수천 배/ 메모리 대비 수만 배 느림

3. 언제 디스크 I/O가 발생하는지?

상황 설명
풀 테이블 스캔 인덱스 없이 테이블 전체를 읽을 때
인덱스 미사용 WHERE 조건이 인덱스를 타지 않을 때 (위의 풀 테이블 스캔과 같은 맥락)
버퍼 풀 부족 자주 쓰는 데이터가 메모리에 없어서 디스크에서 읽어올 때
대용량 정렬/조인 ORDER BY, GROUP BY, JOIN이 메모리를 초과해 임시 디스크 파일을 쓸 때
트랜잭션 로그 기록 INSERT/UPDATE/DELETE 시 redo log, undo log 디스크 기록

 

기본적으로 DBMS는 디스크에 데이터를 저장해둔다.

처음에 버퍼 풀에 데이터가 없을 때는 전부 디스크에서 읽어와야 하기 때문에 디스크 I/O가 발생한다.

 

※ 버퍼 풀(Buffer Pool): 디스크에서 읽은 데이터를 메모리에 캐싱해두는 공간. 여기서 히트하면 디스크 I/O가 발생하지 않음.

  • 히트 (Hit) : 버퍼 풀에서 원하는 데이터를 찾았다는 뜻. 반대로 못 찾으면 미스(Miss) 라고 함.

쿼리 실행
   ↓
버퍼 풀(메모리) 확인
   ↓
있음 → 바로 반환 (디스크 I/O 없음)
없음 → 디스크에서 페이지 읽어옴 → 버퍼 풀에 올림 → 반환

 

서버 재시작 직후처럼 버퍼 풀이 비어있는 상태를 Cold Cache 상태라고 한다. 

이때는 모든 데이터를 디스크에서 읽어야 하니까 쿼리가 평소보다 훨씬 느리고, 시간이 지나면서 자주 쓰는 데이터가 메모리에 쌓이면서 점점 빨라지는데, 이걸 워밍업(Warm-up) 이라고 부르기도 함.

(참고문서 : https://www.geeksforgeeks.org/system-design/cold-and-warm-cache-in-system-design/ )

 

4. 논리적I/O VS 물리적 I/O

  • 논리적 I/O (Logical I/O)
    • 쿼리가 요청한 총 페이지 접근 횟수. 
    • 버퍼 풀에 있든 디스크에 있든 상관없이, 쿼리를 처리하면서 읽으려고 시도한 횟수
  • 물리적 I/O (Physical I/O)
    • 그 중에서 실제로 디스크까지 가서 읽어온 횟수
    • 버퍼 풀에 없어서 디스크를 실제로 건드린 경우만 해당
  • 논리적 I/O가 근본적인 튜닝 지표임. 물리적 I/O는 캐시가 따뜻해지면 자연히 줄어들지만, 논리적 I/O가 크면 아무리 캐시가 좋아도 한계가 있기 때문.

 

페이지(Page) / 블록(Block) : DBMS가 디스크와 메모리 사이에서 데이터를 주고받는 최소 단위

  • MySQL InnoDB 기준 기본 16KB, Oracle은 기본 8KB(변경 가능)
  • Oracle은 블록이라 하고, MySQL 은 페이지라 하는 등 DBMS에 따라 부르는 명칭이 상이함
  • 행(Row) 1건만 필요해도 그 행이 속한 페이지 전체를 읽어옴. 1바이트가 필요해도 16KB를 통째로 읽음
  • 왜 페이지/블록 단위로 읽는지?
    • 디스크는 특성상 연속된 데이터를 한 번에 읽는 게 훨씬 효율적임
    • 디스크 헤드가 계속 이동해야 하는 상황이 발생하면 느려짐
    • "어차피 근처 데이터도 곧 필요할 거니까 묶어서 읽자" 
  • 한 페이지에 행이 많이 담길수록 같은 페이지 수로 더 많은 데이터를 읽을 수 있어서 효율적임
  • 반대로 행 하나가 너무 크면 페이지당 담기는 행 수가 줄어들어서 I/O가 늘어남
  • 대략적인 페이지 구조
    • ( MySQL InnoDB 기준)
┌─────────────────────────┐
│        Page Header       │  페이지 메타정보
├─────────────────────────┤
│  Row1 | Row2 | Row3 ...  │  실제 데이터
├─────────────────────────┤
│        Page Footer       │  체크섬 등
└─────────────────────────┘

 

 

 

논리적 I/O와 물리적 I/O의 관계

논리적 I/O = 버퍼 풀 히트 + 물리적 I/O

 

(예시) 쿼리가 페이지 100개를 접근했는데,

  • 90개는 버퍼 풀에 있었고
  • 10개는 없어서 디스크에서 읽었다면

→ 논리적 I/O: 100, 물리적 I/O: 10

 

위를 말로 풀어서 쓰면 

  • 논리적 I/O 100 = 페이지 100개를 접근하려 했다
  • 물리적 I/O 10  = 그 중 10개는 디스크에서 읽어왔다