한 줄 정의

디스크 I/O는 컴퓨터에서 가장 느린 작업이며, 데이터베이스 성능의 대부분은 디스크 헤드의 이동(랜덤 I/O)을 얼마나 줄이느냐 에 달려 있습니다.

쉽게 말하면

HDD를 LP 레코드 에 비유할 수 있습니다.

  • 연속된 곡을 순서대로 듣는 것은 빠릅니다 (순차 I/O)
  • 이 곡 조금, 저 곡 조금 건너뛰며 듣는 것은 바늘을 계속 옮겨야 하므로 느립니다 (랜덤 I/O)

SSD는 USB 메모리 에 비유할 수 있습니다.

  • 기계적 움직임이 없으므로 어디를 읽든 거의 같은 속도입니다
  • 하지만 여전히 “한 번에 한 블록씩 읽는 비용”은 존재합니다

결국 “디스크에 요청을 몇 번 하느냐”가 성능을 좌우하고, 인덱스의 목적은 이 요청 횟수를 줄이는 것 입니다.

왜 중요한가?

데이터베이스 성능 튜닝의 출발점은 “어디서 시간이 쓰이는가?” 를 아는 것입니다.

구성 요소상대적 속도
CPU 연산매우 빠름
메모리 접근빠름
SSD I/O느림 (메모리의 1/1000 수준)
HDD 랜덤 I/O매우 느림 (SSD의 1/100 수준)
네트워크 I/O상황에 따라 매우 느림

DB 쿼리에서 99%의 시간은 디스크 I/O에서 소비 됩니다. 따라서 CPU를 최적화하는 것보다 I/O를 줄이는 것 이 훨씬 효과적이며, 그 도구가 바로 인덱스입니다.

핵심 내용

HDD와 SSD의 차이

flowchart LR
    subgraph HDD["HDD (자기 디스크)"]
        direction TB
        Arm["디스크 헤드<br/>(물리적 이동)"]
        Platter["회전 플래터"]
        Arm --> Platter
    end

    subgraph SSD["SSD (플래시 메모리)"]
        direction TB
        Controller["컨트롤러"]
        Flash["플래시 셀<br/>(전자적 접근)"]
        Controller --> Flash
    end

    HDD -.->|기계 장치| 느림["랜덤 I/O가 치명적"]
    SSD -.->|전자 장치| 빠름["랜덤 I/O 페널티가 작음"]
HDD의 치명적 약점

HDD는 디스크 헤드를 물리적으로 이동 시켜야 데이터를 읽을 수 있습니다.

  • 플래터가 분당 수천 번 회전합니다
  • 원하는 트랙으로 헤드를 이동하는 탐색 시간(seek time) 이 발생합니다
  • 원하는 섹터가 헤드 밑으로 올 때까지 기다리는 회전 지연(rotational latency) 이 발생합니다

데이터가 흩어져 있으면 이 두 지연이 매 읽기마다 발생합니다.

SSD의 강점과 한계

SSD는 기계적 이동이 없어 랜덤 I/O 페널티가 훨씬 작지만, 여전히 블록 단위로 읽어야 합니다. 또한 쓰기 시 페이지 지우기(erase) 비용 이 별도로 존재합니다.

SSD가 보편화됐다고 해서 인덱스가 덜 중요해진 것은 아닙니다. I/O 요청 횟수 자체를 줄이는 것 은 여전히 중요하며, 버퍼 풀 캐시 히트율도 인덱스 설계에 달려 있습니다.

랜덤 I/O vs 순차 I/O

구분설명특징
순차 I/O연속된 블록을 한 번에 읽기디스크 헤드 이동 1회
랜덤 I/O흩어진 블록을 각각 읽기블록마다 헤드 이동
쿼리 튜닝이 하는 일

쿼리 튜닝의 실체는 랜덤 I/O를 순차 I/O로 바꾸거나, 아예 I/O 횟수를 줄이는 것 입니다.

flowchart TD
    subgraph BAD["나쁜 실행 계획"]
        B1["랜덤 I/O 1000번"]
    end

    subgraph GOOD1["방법 1: 순차 I/O로 전환"]
        G1["범위 조건 + 인덱스 정렬<br/>→ 연속된 페이지 읽기"]
    end

    subgraph GOOD2["방법 2: I/O 자체 제거"]
        G2["커버링 인덱스<br/>→ 인덱스만으로 응답"]
    end

    BAD --> GOOD1
    BAD --> GOOD2
  • 방법 1: 인덱스 레인지 스캔으로 연속된 리프 페이지를 순차 읽기
  • 방법 2: 커버링 인덱스로 테이블 접근 자체를 없애기

페이지/블록 단위 I/O

DB는 레코드 1건이 아니라 페이지(MySQL 기본 16KB) 단위로 읽고 씁니다.

  • 레코드 1건만 필요해도 16KB를 통째로 디스크에서 읽습니다
  • 따라서 한 페이지에 많은 레코드가 담길수록 I/O 효율이 좋아집니다
  • 이것이 PK 크기와 레코드 크기가 성능에 영향을 주는 이유 입니다

페이지 크기가 16KB인 상황에서, 4KB 레코드는 한 페이지에 4건, 40바이트 레코드는 한 페이지에 400건 들어갑니다. 같은 양의 데이터를 읽어도 I/O 횟수가 100배 차이 납니다.

인덱스가 I/O를 줄이는 방식

인덱스가 도움이 되는 지점은 명확합니다.

  1. 풀 스캔보다 훨씬 적은 페이지만 읽음 — 리프 페이지의 필요 부분만 탐색
  2. 정렬된 상태로 저장 — 범위 검색 시 순차 I/O가 가능
  3. 리프 페이지 연결 리스트 — B-Tree의 리프는 좌우로 연결되어 있어 다음 페이지 이동이 쉬움
  4. 커버링 인덱스 — 데이터 파일을 아예 안 읽고 끝낼 수 있음

단, 인덱스 스캔 자체도 랜덤 I/O 입니다. 읽어야 할 레코드가 전체의 20~25%를 넘으면 옵티마이저가 풀 스캔(순차 I/O)을 선택하는 이유가 이것입니다.

내 생각

  • SSD 시대에도 “I/O를 줄여라”는 원칙은 여전히 유효합니다. 물리적 seek time은 사라졌지만, 버퍼 풀 캐시 효율네트워크 왕복 시간 이 병목이 되기 때문입니다. 인덱스 설계 원칙은 스토리지 기술 변화와 무관하게 유효합니다.

  • “풀 스캔이 항상 나쁘다”는 오해도 조심해야 합니다. 읽어야 할 레코드가 테이블의 대부분이라면, 순차 I/O 풀 스캔이 랜덤 I/O 인덱스 스캔보다 훨씬 빠릅니다. 이것이 옵티마이저가 통계 정보로 판단하는 지점입니다.

  • 실무에서 I/O 문제를 진단할 때는 SHOW ENGINE INNODB STATUS의 I/O 섹션과 performance_schemafile_summary_by_event_name을 함께 봐야 합니다. 어떤 파일에 I/O가 집중되는지 알면 튜닝 방향이 명확해집니다.

관련 개념

출처

  • Real MySQL 8.0 (1권), 8.1 디스크 읽기 방식