428. 테이블 풀 스캔 (Table Full Scan / FTS)

⚠️ 이 문서는 데이터베이스 검색 시 무조건 기피해야 할 악당으로 여겨지지만, **사실 특정 상황(데이터가 적거나 너무 많이 조회할 때)에서는 인덱스보다 10배 이상 빠르고 효율적인 궁극의 데이터 읽기 방식인 '테이블 풀 스캔'**을 다룹니다.

핵심 인사이트 (3줄 요약)

  1. 본질: 디스크에 저장된 테이블의 첫 번째 데이터 블록부터 마지막 블록까지 차례대로(Sequential) 전부 다 읽어 들여 메모리로 가져오는 가장 무식하지만 정직한 스캔 방식이다.
  2. 강점 (Multi-block I/O): 인덱스가 '한 번에 한 줄씩' 디스크를 건너뛰며 읽는다면(Random I/O), 풀 스캔은 '한 번에 수십~수백 줄'을 통째로 퍼 올리기(Multi-block I/O) 때문에 대용량 데이터를 처리할 때 압도적으로 빠르다.
  3. 발생 조건: WHERE 조건에 인덱스가 아예 없거나, 옵티마이저가 판단했을 때 전체 데이터의 약 10~15% 이상을 조회해야 한다고 계산되면 자동으로 발동한다.

Ⅰ. 개요: 우직한 청소부 (Context & Necessity)

백엔드 개발자들은 쿼리 실행 계획에 TABLE ACCESS FULL이 뜨면 사색이 된다. "아뿔싸, 인덱스를 안 탔네! 서버 터지겠다!"

하지만 100페이지짜리 얇은 만화책에서 '호랑이'라는 단어를 찾는다고 치자.

  • 인덱스 방식: 맨 뒤 색인에서 '호랑이'를 찾는다 $\rightarrow$ 3쪽, 15쪽, 28쪽, 90쪽에 있다는 걸 확인한다 $\rightarrow$ 다시 앞으로 와서 3쪽 펴고, 15쪽 펴고... (엄청 귀찮음)
  • 풀 스캔 방식: 그냥 1쪽부터 100쪽까지 후루룩 읽는다. (단 1분이면 끝남)

이처럼 테이블 크기가 매우 작거나(Small Table), 전체 데이터의 상당 부분을 긁어와야 할 때 데이터베이스는 멍청한 인덱스 스캔을 포기하고 풀 스캔이라는 우직한 선택을 한다.

📢 섹션 요약 비유: 풀 스캔은 **'아파트 전단지 돌리기'**와 같습니다. 특정 호수를 골라서 엘리베이터를 타고 오르락내리락하는 것(인덱스 스캔)보다, 그냥 1층부터 10층까지 모든 집에 걸어 올라가며 한꺼번에 쫙 뿌리고 오는 게 훨씬 효율적일 때가 있습니다.


Ⅱ. 풀 스캔이 빠른 이유: Multi-block I/O ★

하드디스크는 바늘(Head)을 움직여서 데이터를 찾는 시간(Seek Time)이 제일 오래 걸린다.

  • 인덱스 스캔 (Single-block I/O): 디스크 바늘이 1번 방 갔다가, 900번 방 갔다가, 3번 방으로 미친 듯이 점프한다. (디스크 비명 지름)
  • 풀 스캔 (Multi-block I/O): 바늘을 딱 1번 방에 한 번만 갖다 대고, 그대로 100번 방까지 멈추지 않고 한 번에 싹 긁어모은다.

이 **'연속적인 순차 읽기(Sequential Read)'**의 힘 때문에, 대용량 데이터를 처리하는 집계 쿼리(SUM, GROUP BY)나 데이터 웨어하우스(OLAP) 환경에서는 무조건 풀 스캔이 인덱스 스캔을 이긴다.


Ⅲ. 풀 스캔이 발생하는 4가지 경우

개발자의 실수로 발생하기도 하고, 옵티마이저의 현명한 선택으로 발생하기도 한다.

1. 인덱스가 아예 없을 때 (개발자 실수)

  • SELECT * FROM Users WHERE name = '김철수' (이름 컬럼에 인덱스 안 만듦)

2. 인덱스 컬럼을 가공했을 때 (개발자 뼈아픈 실수)

  • SELECT * FROM Users WHERE SUBSTRING(name, 1, 1) = '김'
  • 인덱스는 '김철수'로 예쁘게 정렬되어 있는데, 함수(SUBSTRING)를 씌워버리면 인덱스 책갈피가 다 망가져서 무용지물이 된다. (인덱스 타게 하려면 name LIKE '김%'로 써야 함)

3. 손익 분기점 (Table Fetch Break-even Point) 돌파

  • 옵티마이저의 계산 결과, 가져와야 할 데이터가 전체 테이블의 **약 10~15%**를 넘어가면(Selectivity가 나쁘면) 인덱스를 버리고 풀 스캔을 택한다.

4. 테이블이 너무 작을 때

  • 데이터가 100건밖에 안 되면 인덱스 트리를 타는 비용이 더 든다.
┌──────────────────────────────────────────────────────────────┐
│           인덱스 스캔 vs 테이블 풀 스캔(Full Scan) 성능 교차점 시각화        │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│ 속도(Cost)                                                     │
│   ▲                                                          │
│   │                                                          │
│   │                                       / ◀─ (인덱스 스캔)  │
│   │                                      /                   │
│   │             (교차점: 10~15% 구간)   /                     │
│   │----------------------X-------------------- ◀─ (풀 스캔)   │
│   │                     /                                    │
│   │                    /                                     │
│   │                   /                                      │
│   │                  /                                       │
│   └───────────────────────────────────────────────▶ 추출 건수  │
│                                                              │
│ ★ 결론: 뽑아올 데이터가 많아질수록 인덱스 스캔은 급격히 느려지고 풀스캔이 이긴다.│
└──────────────────────────────────────────────────────────────┘

Ⅳ. 결론

"풀 스캔은 무조건 나쁘다는 편견을 버려라." 물론 트래픽이 쏟아지는 웹 서비스(OLTP)에서 회원 1명을 찾기 위해 풀 스캔이 도는 것은 치명적인 장애 원인이 된다. 하지만 야간에 배치(Batch) 프로그램을 돌려 전교생의 평균 점수를 구하거나, 1년 치 통계 데이터를 뽑아낼 때 옵티마이저가 풀 스캔을 탔다면 그것은 지극히 정상적이고 훌륭한 실행 계획이다. 데이터베이스 튜닝의 고수는 풀 스캔을 무조건 인덱스로 바꾸는 사람이 아니라, 이 쿼리가 풀 스캔을 타는 게 맞는지 틀린지를 정확히 분별하는 사람이다.


📌 관련 개념 맵

  • 대척점 개념: Index Scan (429번 문서)
  • 상위 개념: 실행 계획 (Execution Plan - 421번 문서)
  • 하드웨어 원리: Sequential I/O (순차 읽기), Random I/O (랜덤 읽기)
  • 관련 최적화: Parallel Query (병렬 쿼리 - 풀 스캔을 여러 스레드가 나눠서 긁어오는 기술)

👶 어린이를 위한 3줄 비유 설명

  1. 풀 스캔은 내가 가장 좋아하는 과자를 찾으려고 마트의 '1번 진열대부터 끝까지' 모든 과자를 다 눈으로 확인하며 지나가는 거예요.
  2. 시간이 엄청 오래 걸릴 것 같죠? 하지만 만약 내가 과자를 '100개' 사야 한다면, 카트를 밀고 한 번 쭉 직진하면서 100개를 담는 게 최고예요.
  3. 과자를 1개 찾을 땐 점원에게 물어보는(인덱스) 게 빠르지만, 왕창 쓸어 담을 땐 무식하게 직진(풀 스캔)하는 게 제일 빠르답니다!