개발기술/데이터베이스
데이터 베이스의 디스크 읽기방식
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을 택하기도 합니다.
실무에서의 판단 방법
- 실행계획 (EXPLAIN or EXPLAIN ANALYZE)
→ 실제 인덱스를 쓰는지, Full Scan인지, 예상 row 수는 얼마인지 확인
→ MySQL/MariaDB: EXPLAIN SELECT ...
→ PostgreSQL: EXPLAIN ANALYZE SELECT ... - 데이터 분포를 파악하자 (ANALYZE, 통계 정보)
- 특정 칼럼의 distinct 개수 (선택도)
- 특정 조건에서 평균적으로 몇 건이 걸리는지
- 테스트 환경에서 쿼리 벤치마크
→ 1천 건, 10만 건, 100만 건 등으로 증가시켜 성능 추이 관찰 - 옵티마이저 힌트 (힌트나 FORCE INDEX)로 테스트 가능
→ 인덱스를 강제로 사용하거나 무시하고 성능 비교