개발기술/데이터베이스

데이터 베이스의 디스크 읽기방식

bsh6226 2025. 6. 18. 09:40

 

디스크 I/O는 ‘읽어야할 row 개수’보다 ‘접근 패턴’이 중요

 

디스크는 블록 단위로 읽는다

 

  • 디스크에서 데이터를 읽을 때는 row 단위가 아니라 보통 4KB, 8KB 블록 단위로 읽습니다.
  • 하나의 블록에는 수십~수백 개의 row가 들어있습니다.
  • 즉, 100만 row를 Full Scan한다 해도, 디스크는 수천 개의 블록만 읽으면 충분합니다.

 

 

 

Full Scan

 

  • 순차 I/O (Sequential I/O): 디스크가 물리적으로 인접한 블록을 한 번에 쭉 읽는 방식
    • 하드디스크의 경우, 디스크 헤드가 거의 움직이지 않음 (빠름)
    • SSD도 내부 채널 병렬성 활용이 가능 (빠름)
    • OS나 DB가 미리 예측해서 prefetch → 성능 극대화
  • Full Scan은 하드디스크나 SSD가 물리적으로 정렬된 데이터를 블록 단위로 연속해서 읽기 때문에 OS나 DB가 prefetch도 할 수 있고, 버스·디스크 캐시 활용률도 높습니다.

 

랜덤 I/O

 

  • 랜덤 I/O (Random I/O): 비연속적인 블록을 이리저리 점프하며 읽는 방식으로  ‘이동 비용’이 큼
    • 하드디스크(HDD)에서는 디스크 헤드가 물리적으로 이동해야 해서 매우 느림 (ms 단위 I/O)
    • SSD도 내부 주소 매핑 & 제어기 비용이 있어 HDD보다 빠르지만, 순차 I/O보다 여전히 느림
    • OS도 미리 예측하기 어려워 prefetch 불가 → 효율 ↓
  •  예시: 인덱스를 따라 각 row의 주소로 접근 → 이 row는 여기, 다음 row는 다른 블록 → 디스크가 매번 점프

 

 

랜덤 I/O는 ‘블록 재사용이 안 되는’ 단점이 있음

  • 인덱스를 타면 DB는 특정 조건의 row를 가진 주소(pointer) 를 찾습니다.
  • 이 주소를 따라가면 row가 있는 블록을 읽어야 하는데,
  • 이 블록이 이전에 안 읽었던 것이면 디스크에서 새로 읽어야 하며,
  • 위치가 다 제각각이면, 계속 디스크를 건너뛰며 접근하게 되어 → 랜덤 I/O 발생

➡️ 랜덤 I/O는 블록을 매번 새로 디스크에서 찾아야 해서 비싸다

구분 특징 예시  속도
순차 I/O 연속된 블록을 한 번에 읽음 책 한 권을 쭉 읽기 빠름
랜덤 I/O 서로 다른 위치를 뛰어다님 책 여러 권의 특정 페이지마다 찾기 느림

 

그러므로 인덱스를 써도 항상 빠르진 않다

왜냐하면?

  • 인덱스 자체는 “주소 목록”일 뿐입니다.
  • 인덱스를 타면 → 인덱스 → 실제 row 주소로 가서 → row를 메모리에 로딩해야 합니다.
  • 이때 row들이 서로 멀리 떨어져 있으면 랜덤 I/O가 발생합니다.
상황 인덱스 성능 이유
인덱스 조건이 아주 희귀할 때 👍 빠름 소수의 row만 가져오니 랜덤 I/O 부담도 적음
조건이 널널해서 row를 수천~수만 건 가져올 때 👎 느릴 수 있음 랜덤 I/O가 폭발적으로 늘어남
조건 없이 전체 스캔할 때 🔁 Full Scan이 오히려 더 빠름 순차 I/O + prefetch + 블록 단위 버퍼링 활용 가능
 

 

예시 시나리오

  • 총 6억 row (예: 로그 테이블)
SELECT * FROM logs
WHERE camera_no = 123;

 

 

조건 필터 결과 인덱스 vs Full Scan 예상
camera_no = 1 2만 건 (0.003%) ✅ 인덱스 탐색이 훨씬 빠름
camera_no = 123 200만 건 (0.33%) ✅ 아직 인덱스가 빠름
camera_no = 123 AND yyyymmdd = '20250616' 60,000건 (0.01%) ✅ 인덱스 + 조건 필터 유리
camera_no = 1 OR status = 'ALERT' 3천만 건 (5%) ⚠️ 경계, 옵티마이저 판단 따라 다름
camera_no IN (1,2,3,...,100) 2억 건 (33%) ❌ Full Scan이 훨씬 유리함

 

실제 DB 엔진은 어떻게 하냐?

DB는 **통계 정보(통계 힌트, 히스토그램)**를 기반으로 다음을 판단합니다:  "이 조건이면 인덱스 타는 게 빠를까? Full Scan이 빠를까?” 이걸 **쿼리 옵티마이저(Query Optimizer)**가 결정합니다. 그래서 때로는 인덱스를 무시하고 Full Scan을 택하기도 합니다.

 

실무에서의 판단 방법

  1. 실행계획 (EXPLAIN or EXPLAIN ANALYZE)
    → 실제 인덱스를 쓰는지, Full Scan인지, 예상 row 수는 얼마인지 확인
    → MySQL/MariaDB: EXPLAIN SELECT ...
    → PostgreSQL: EXPLAIN ANALYZE SELECT ...
  2. 데이터 분포를 파악하자 (ANALYZE, 통계 정보)
    • 특정 칼럼의 distinct 개수 (선택도)
    • 특정 조건에서 평균적으로 몇 건이 걸리는지
  3. 테스트 환경에서 쿼리 벤치마크
    → 1천 건, 10만 건, 100만 건 등으로 증가시켜 성능 추이 관찰
  4. 옵티마이저 힌트 (힌트나 FORCE INDEX)로 테스트 가능
    → 인덱스를 강제로 사용하거나 무시하고 성능 비교