Index Full Scan·Fast Full Scan·Table Full Scan: 정렬·I/O·Covering
Index Full Scan과 Index Fast Full Scan의 읽기 순서, 정렬 보장, 멀티블록·병렬 가능성, Covering 조건을 Table Full Scan과 비교합니다.
핵심 요약
INDEX FULL SCAN, INDEX FAST FULL SCAN, TABLE ACCESS FULL은 모두 넓은 범위를 읽을 수 있지만 읽는 객체·순서·I/O 방식·결과 완성 방식이 다릅니다.
| 항목 | INDEX FULL SCAN | INDEX FAST FULL SCAN | TABLE ACCESS FULL |
|---|---|---|---|
| 읽는 객체 | B-tree 전체 Leaf Entry | Index Segment의 Block | Table Segment의 Scan 대상 Block |
| 순서 | Key 순서 보존 | Key 순서 보장 안 함 | Row 순서 보장 안 함 |
| 대표 I/O | Ordered·Single-Block 성격 | Multiblock 가능 | Multiblock 가능 |
| 병렬 후보 | 순서 유지 때문에 제한적 | 조건에 따라 가능 | 조건에 따라 가능 |
| Table Access | Covering이면 생략, 아니면 ROWID Access 가능 | 일반적으로 Query를 Index만으로 완성할 때 후보 | Table 자체를 읽음 |
| Sort | ORDER BY와 호환되면 생략 가능 | 일반적으로 별도 Sort 필요 | 일반적으로 별도 Sort 필요 |
| 대표 목적 | 전체 Index를 정렬 순서로 활용 | Table보다 작은 Covering Index의 전체 Scan | 많은 Row·Column의 직접 Scan |
INDEX FULL SCAN
→ Index 한쪽 끝 Leaf로 이동
→ Leaf Link를 Key 순서로 전체 순회
INDEX FAST FULL SCAN
→ Index Segment Block 전체를 넓게 읽음
→ Key 순서 포기
→ Index 안의 값만으로 결과 생성
TABLE ACCESS FULL
→ Table High Water Mark 아래 Scan 대상 Block 읽기
→ Predicate 적용
FAST는 모든 상황에서 가장 빠르다는 뜻이 아닙니다. 정렬 순서를 포기해 Multiblock·병렬 처리 후보가 되는 Access Path의 이름입니다.
이 이론의 범위
이 이론은 SQLP의
SQL 고급활용 및 튜닝 → 인덱스 튜닝 → 인덱스 스캔 방식범위에서 Index Full Scan·Fast Full Scan·Table Full Scan을 비교합니다.
학습 목표
이 이론을 학습한 뒤에는 다음을 설명할 수 있어야 합니다.
- Range Scan과 Index Full Scan의 범위 차이를 설명한다.
- Index Full Scan의 Key 순서 보존과 Descending Scan을 설명한다.
- Index Full Scan도 Covering이면 Table Access 없이 끝날 수 있음을 설명한다.
- Index Fast Full Scan의 비정렬·Multiblock·병렬 특성을 설명한다.
- Fast Full Scan이 일반적으로 Query를 Index만으로 완성해야 하는 이유를 설명한다.
- 일반 B-tree의 All-Key-NULL 미저장 규칙과
COUNT(*)·COUNT(col)을 설명한다. - Leaf Blocks와 전체 Index Segment Block의 차이를 설명한다.
- Table Full Scan의 HWM·Small Table·병렬 이점을 설명한다.
- Partition·압축·정렬·Partial Fetch가 후보 비용에 미치는 영향을 설명한다.
- A-Rows·Buffers·Reads·Sort·Table Access·Elapsed로 세 경로를 검증한다.
1. 먼저 Range Scan 가능성을 확인한다
다음 Index를 가정합니다.
CREATE INDEX emp_dept_empno_ix
ON emp(deptno, empno);
1.1 Index Range Scan
SELECT empno
FROM emp
WHERE deptno = 10;
INDEX RANGE SCAN EMP_DEPT_EMPNO_IX
deptno=10의 Start Key로 이동해 해당 Key Group이 끝나면 멈춥니다.
1.2 Index Full Scan
SELECT deptno, empno
FROM emp
WHERE deptno IS NOT NULL
ORDER BY deptno, empno;
INDEX FULL SCAN EMP_DEPT_EMPNO_IX
좁은 Start·Stop Range가 아니라 Index 한쪽 끝부터 전체 Leaf Entry를 읽습니다.
Range Scan
→ 일부 Key 범위
Index Full Scan
→ 전체 Leaf Entry
전체 Scan 후보를 비교하기 전에 Predicate를 Sargable하게 바꾸거나 더 적절한 Leading Column Index로 Range를 줄일 수 있는지 먼저 확인합니다.
2. INDEX FULL SCAN
2.1 동작
Ascending Full Scan은 Root·Branch를 통해 왼쪽 끝 Leaf로 이동하고 Leaf Link를 따라 전체 Entry를 읽습니다.
Root·Branch
↓
왼쪽 끝 Leaf
→ 다음 Leaf
→ ...
→ 오른쪽 끝
Descending Full Scan은 반대 끝에서 시작합니다.
INDEX FULL SCAN DESCENDING
2.2 Key 순서와 Sort
SELECT deptno, empno
FROM emp
WHERE deptno IS NOT NULL
ORDER BY deptno, empno;
Index Key 순서와 ORDER BY가 호환되면 SORT ORDER BY를 생략할 수 있습니다.
ORDER BY deptno DESC, empno DESC
전체 방향을 뒤집은 정렬은 INDEX FULL SCAN DESCENDING으로 지원할 수 있습니다. 혼합 방향은 해당 방향으로 정의한 Index가 필요할 수 있습니다.
업무상 순서는 반드시 SQL의 ORDER BY로 명시합니다. 현재 Plan이 정렬된 Row를 생산한다는 사실만으로 ORDER BY 없는 결과 순서를 보장하지 않습니다.
2.3 Covering과 Table Access
Index Full Scan은 두 형태가 모두 가능합니다.
Covering
INDEX FULL SCAN
→ Index만 읽고 결과 완성
Non-Covering
TABLE ACCESS BY INDEX ROWID
INDEX FULL SCAN
→ Index 전체에서 ROWID 생성
→ 필요한 Table Column 읽기
따라서 “Index Full Scan에는 항상 Table Access가 붙는다”는 설명은 부정확합니다.
2.4 Filter 비용
SELECT deptno, empno
FROM emp
WHERE empno >= 9000;
선두 deptno가 없어 좁은 Range를 만들기 어렵다면 전체 Index를 읽고 empno를 Filter할 수 있습니다.
INDEX FULL SCAN EMP_DEPT_EMPNO_IX
filter(empno>=9000)
A-Rows가 적음
≠ 적은 Entry만 읽음
Index Buffers·Reads로 전체 Leaf 작업량을 확인합니다.
3. INDEX FAST FULL SCAN
3.1 동작
Fast Full Scan은 Leaf Link를 Key 순서로 따라가지 않고 Index Segment Block을 넓게 읽습니다.
Index Segment Block Scan
→ Root·Branch·Leaf Block I/O 가능
→ 결과 값은 Leaf Entry에서 생성
→ Key 순서 보장 안 함
대표 Operation입니다.
INDEX FAST FULL SCAN EMP_DEPT_EMPNO_IX
3.2 Multiblock·병렬
Full Table Scan과 유사하게 Multiblock I/O를 사용할 수 있고 조건에 따라 병렬 실행 후보가 됩니다.
Index Full Scan
→ Ordered Scan
→ Key 순서 활용
Index Fast Full Scan
→ Unordered Block Scan
→ Multiblock·Parallel 가능
DB_FILE_MULTIBLOCK_READ_COUNT, System Statistics, Storage 처리량과 병렬 환경이 예상 비용에 영향을 줄 수 있습니다.
3.3 Index-only 조건
Fast Full Scan은 일반적으로 Query에 필요한 정보를 Index 안에서 얻을 수 있을 때 Table Full Scan의 대안이 됩니다.
SELECT deptno, empno
FROM emp
WHERE deptno IS NOT NULL;
필요한 Predicate·Projection을 Index에서 처리할 수 있습니다.
INDEX FAST FULL SCAN EMP_DEPT_EMPNO_IX
Index에 없는 ename을 조회한다면 작은 Index 전체를 읽고 대량 ROWID로 Table을 방문하는 것보다 Table Full Scan이 더 저렴할 가능성이 큽니다.
3.4 정렬 미보장
SELECT deptno, empno
FROM emp
ORDER BY deptno, empno;
Fast Full Scan이 선택되면 다음과 같이 Sort가 필요할 수 있습니다.
SORT ORDER BY
INDEX FAST FULL SCAN EMP_DEPT_EMPNO_IX
Sort·TEMP가 큰 경우 Ordered Index Full Scan과 비교합니다.
4. TABLE ACCESS FULL
Table Full Scan은 Table Segment의 High Water Mark 아래 Scan 대상 Block을 읽고 각 Row에 Predicate를 적용합니다.
TABLE ACCESS FULL EMP
유리할 수 있는 상황입니다.
- Table의 많은 Row·Block이 필요
- Query가 Index에 없는 Column을 많이 조회
- Table이 작음
- 적합한 Index가 없음
- Index 경로의 ROWID Table Access가 과도
- 병렬·대량 I/O 처리량이 유리
- Partition Pruning 후 일부 Partition만 Scan
SELECT empno, ename, sal, hiredate, job, mgr, deptno
FROM emp;
대부분의 Row와 폭넓은 Column이 필요하면 Table을 직접 읽는 편이 단순하고 저렴할 수 있습니다.
5. 같은 SQL에서 세 후보를 비교한다
SELECT deptno, empno
FROM emp
WHERE deptno IS NOT NULL;
후보 A: Index Full Scan
Key 순서로 전체 Leaf Entry
→ ORDER BY가 있으면 Sort 제거 가능
후보 B: Index Fast Full Scan
Index Segment 전체 비정렬 Scan
→ Index가 Table보다 작으면 유리 가능
→ Multiblock·Parallel 후보
후보 C: Table Full Scan
Table HWM 아래 Block Scan
→ Table이 작거나 Index 이점이 작으면 유리
비교 항목입니다.
| 항목 | 질문 |
|---|---|
| Range 가능성 | 전체 Scan 전에 좁은 Range로 바꿀 수 있는가 |
| Projection | 필요한 Column이 Index에 모두 있는가 |
| Row 완전성 | Index가 Query 대상 Row를 모두 저장하는가 |
| 정렬 | ORDER BY와 Key 순서·방향이 호환되는가 |
| 크기 | Index 전체 Block과 Table HWM Block은 얼마인가 |
| I/O | Ordered Single-Block과 Multiblock 중 무엇이 유리한가 |
| 병렬 | 병렬 Scan이 필요한가, Resource 비용은 어떤가 |
| 후속 작업 | Sort·Join·Aggregate·Table Access 비용은 어떤가 |
6. COUNT와 B-tree NULL 저장 규칙
일반 B-tree는 모든 Index Key Column이 NULL인 Row를 저장하지 않습니다.
6.1 COUNT(*)
SELECT COUNT(*)
FROM emp;
Index로 Table 전체 Row를 세려면 모든 Table Row가 Index Entry에 존재해야 합니다.
CREATE UNIQUE INDEX emp_pk
ON emp(empno);
empno가 NOT NULL이면 모든 Row가 Index Entry를 가지므로 다음이 후보가 될 수 있습니다.
SORT AGGREGATE
INDEX FAST FULL SCAN EMP_PK
6.2 COUNT(nullable_col)
CREATE INDEX emp_comm_ix
ON emp(comm);
SELECT COUNT(comm)
FROM emp;
COUNT(comm)은 NULL을 제외합니다. 단일 Nullable Column B-tree도 NULL Row를 저장하지 않으므로 Query 의미와 Index Entry 집합이 맞을 수 있습니다.
6.3 Nullable Index로 COUNT(*)
SELECT COUNT(*)
FROM emp;
EMP_COMM_IX에는 comm IS NULL Row가 없으므로 이 Index만으로 전체 Row 수를 정확히 만들 수 없습니다.
6.4 복합 Index
CREATE INDEX emp_comm_empno_ix
ON emp(comm, empno);
empno가 NOT NULL이면 (NULL,empno) Entry가 저장되어 모든 Row를 표현할 수 있습니다.
COUNT(*) Index 후보
→ 필요한 모든 Row가 Index에 존재하는지 검증
7. Segment Block과 Leaf Block을 구분한다
SELECT index_name,
blevel,
leaf_blocks,
clustering_factor
FROM user_indexes
WHERE table_name='EMP';
LEAF_BLOCKS는 Leaf Block 통계입니다. Fast Full Scan은 Index Segment를 넓게 읽으므로 Root·Branch·Leaf를 포함한 실제 Segment·Runtime Buffers·Reads를 확인합니다.
SELECT segment_name,
blocks,
bytes
FROM user_segments
WHERE segment_name IN ('EMP','EMP_DEPT_EMPNO_IX');
Partitioned·Compressed Index라면 전체와 대상 Partition의 크기를 구분합니다.
통계와 실제 Segment 상태가 다를 수 있으므로 마지막 분석 시점도 확인합니다.
8. Partition·압축·병렬
8.1 Partitioned Index
Partitioned Index에서는 필요한 Index Partition만 Full·Fast Full Scan할 수 있습니다.
PARTITION RANGE SINGLE
INDEX FAST FULL SCAN
전체 Index 크기가 아니라 Pruning된 Partition의 Block과 Starts를 기준으로 비교합니다.
8.2 Compression
Index Compression은 Segment Block 수와 I/O를 줄일 수 있지만 CPU와 DML 특성이 달라질 수 있습니다.
작은 압축 Index
→ Fast Full Scan 이점 가능
압축·해제 CPU와 DML
→ 별도 검증
8.3 Parallel
Fast Full Scan과 Table Full Scan은 병렬 후보가 될 수 있습니다. Elapsed가 줄더라도 전체 CPU·I/O·PX Server 사용량은 증가할 수 있습니다.
9. Partial Fetch와 전체 Fetch
첫 페이지 20행만 필요한 Query와 전체 결과를 모두 처리하는 Query는 비교 계약이 다릅니다.
첫 페이지 목표
→ 양쪽 모두 첫 20행까지 Fetch
전체 처리 목표
→ 양쪽 모두 End-of-Fetch
Index Full Scan은 정렬된 앞부분을 빨리 제공할 수 있지만 Fast Full Scan·Table Full Scan + Sort는 전체 작업 후 Row를 반환할 수 있습니다.
다음을 확인합니다.
- Fetch Calls
- Rows Processed
- End of Fetch Count
- Sort·TEMP
- Application Row Limit
서로 다른 Fetch 범위의 Elapsed를 직접 비교하지 않습니다.
10. 실행계획 비교
Index Full Scan 후보
SELECT /*+ GATHER_PLAN_STATISTICS INDEX(e emp_dept_empno_ix) */
deptno, empno
FROM emp e
WHERE deptno IS NOT NULL
ORDER BY deptno, empno;
Index Fast Full Scan 후보
SELECT /*+ GATHER_PLAN_STATISTICS INDEX_FFS(e emp_dept_empno_ix) */
deptno, empno
FROM emp e
WHERE deptno IS NOT NULL;
Table Full Scan 후보
SELECT /*+ GATHER_PLAN_STATISTICS FULL(e) */
deptno, empno
FROM emp e
WHERE deptno IS NOT NULL;
SELECT *
FROM TABLE(
DBMS_XPLAN.DISPLAY_CURSOR(
:sql_id,
:child_no,
'ALLSTATS LAST +PREDICATE +ALIAS +NOTE'
)
);
확인합니다.
- Scan Operation과 대상 Object·Partition
- Starts·E-Rows·A-Rows
- Buffers·Reads
- Sort·Memory·TEMP
- Table Access 여부
- Parallel Operation·DOP
- 실제 Fetch 범위
- CPU·Elapsed·P95
- Index·Table Block 수
- DML·Redo·공간과 다른 SQL 회귀
10.1 상위 Buffers 합산 주의
상위 Operation Buffer에는 하위 Row Source 작업이 포함될 수 있습니다. 모든 Line의 Buffers를 단순 합산하지 않고 비용이 집중된 Branch와 Statement 총량을 구분합니다.
11. 선택 예제
Table·Index 통계입니다.
EMP Table Blocks 100,000
EMP_DEPT_EMPNO_IX Segment Blocks 15,000
정렬 없는 Index-only 집계
Index Fast Full Scan
Buffers 15,000
Sort 없음
Table Access 없음
Table Full Scan
Buffers 100,000
Fast Full Scan이 유리할 수 있습니다.
ORDER BY 포함
Index Full Scan
Buffers 12,000
Sort 없음
Index Fast Full Scan
Buffers 15,000
Sort·TEMP 8,000
Ordered Full Scan이 유리할 수 있습니다.
Non-Covering Projection
Index Full Scan
Index Buffers 12,000
Table Buffers 90,000
Table Full Scan
Buffers 100,000
Index 순서 이득보다 Table Random Access가 크면 Full Scan이 유리할 수 있습니다.
12. 자주 혼동하는 판단
| 혼동 | 정확한 기준 |
|---|---|
| Index Full Scan은 일부 범위 Scan이다 | 전체 Leaf Entry를 Key 순서로 읽는다 |
| Index Full Scan에는 항상 Table Access가 붙는다 | Covering이면 Index-only로 끝날 수 있다 |
| Fast Full Scan은 Leaf Link를 빠르게 따라간다 | Segment Block을 비정렬 방식으로 넓게 읽는다 |
| Fast Full Scan은 항상 가장 빠르다 | Sort·크기·병렬·후속 작업을 포함해 판단한다 |
| Fast Full Scan은 Key 순서를 보장한다 | Key 순서를 보장하지 않는다 |
| Fast Full Scan으로 아무 Column이나 조회 가능하다 | 일반적으로 필요한 정보가 Index에 있어야 유력하다 |
| LEAF_BLOCKS가 Fast Full Scan의 정확한 전체 I/O다 | 전체 Segment와 Runtime Buffers·Reads를 확인한다 |
| Nullable 단일 Index로 COUNT(*)가 가능하다 | All-Key-NULL Row 누락 여부를 확인한다 |
| ORDER BY가 없어도 Index Full Scan 순서를 사용한다 | 업무 순서는 ORDER BY로 명시한다 |
| Buffers가 작으면 언제나 빠르다 | I/O 방식·CPU·병렬·Elapsed·Resource를 함께 본다 |
개념 확인 문제
문제를 누르면 바로 아래에서 정답과 해설을 확인할 수 있습니다.
01Index Range Scan과 Index Full Scan의 가장 중요한 차이를 설명하시오.
Range·Full
- Range Scan은 Start·Stop Key로 제한된 Leaf 구간을 읽습니다.
- Index Full Scan은 Index 한쪽 끝에서 반대쪽 끝까지 전체 Leaf Entry를 Key 순서로 읽습니다.
02Index Full Scan의 탐색 과정과 Key 순서 보존 원리를 설명하시오.
Ordered 탐색
- Root·Branch를 통해 왼쪽 끝 또는 오른쪽 끝 Leaf로 이동합니다.
- Leaf Link를 따라 순방향·역방향으로 전체 Entry를 읽습니다.
- 이 때문에 Index Key와 호환되는 ORDER BY를 지원할 수 있습니다.
03Index Full Scan이 Covering일 때와 Non-Covering일 때의 Plan 차이를 설명하시오.
Covering
- 필요한 Predicate·Projection이 Index에 있으면
INDEX FULL SCAN만으로 끝날 수 있습니다. - Index 밖 Column이 필요하면
TABLE ACCESS BY INDEX ROWID가 위에 나타날 수 있습니다. - 전체 ROWID Table Access가 크면 Table Full Scan보다 비쌀 수 있습니다.
04Index Fast Full Scan의 Block 읽기·정렬·Multiblock·병렬 특성을 설명하시오.
Fast Full
- Index Segment의 Block을 Key 순서 없이 읽습니다.
- Multiblock I/O와 병렬 실행 후보가 될 수 있습니다.
- 정렬을 보장하지 않아 ORDER BY에는 Sort가 필요할 수 있습니다.
- Query를 Index 안의 정보로 완성할 때 Table Full Scan 대안이 됩니다.
05Fast Full Scan과 Table Full Scan의 선택 조건을 비교하시오.
Fast Full·Table Full
- 필요한 Column이 Index에 모두 있고 Index가 Table보다 작으면 Fast Full이 유리할 수 있습니다.
- 많은 Table Column·대부분의 Row가 필요하거나 Table이 작으면 Table Full Scan이 유리할 수 있습니다.
- Block 수·병렬·Sort·Elapsed를 실제로 비교합니다.
06COUNT(), COUNT(nullablecol)과 B-tree All-Key-NULL 규칙을 설명하시오.
COUNT·NULL
- COUNT(*)는 모든 Row가 Index Entry에 존재해야 합니다.
- NOT NULL Key가 있는 Index는 전체 Row를 표현할 수 있습니다.
- Nullable 단일 Index는 NULL Row가 없어 COUNT(*)에는 부적합할 수 있습니다.
- COUNT(nullable_col)은 NULL을 제외하므로 Index Entry 집합과 의미가 맞을 수 있습니다.
07LEAFBLOCKS와 실제 Fast Full Scan Segment I/O의 차이를 설명하시오.
Leaf·Segment
- LEAF_BLOCKS는 Leaf Block 통계입니다.
- Fast Full Scan은 Index Segment Block을 넓게 읽으므로 Branch·Root와 실제 Segment 상태도 비용에 영향을 줍니다.
- USER_SEGMENTS와 Runtime Buffers·Reads를 확인합니다.
08Partition·Compression·병렬이 Fast Full Scan 비용에 미치는 영향을 설명하시오.
Partition·Compression·Parallel
- Pruning되면 일부 Index Partition만 Scan할 수 있습니다.
- Compression은 Block 수를 줄일 수 있지만 CPU·DML 비용이 달라집니다.
- Parallel은 Elapsed를 줄일 수 있지만 전체 Resource 사용을 늘릴 수 있습니다.
09Partial Fetch와 전체 Fetch에서 Index Full·Fast Full·Table Full Scan의 비교 차이를 설명하시오.
Fetch
- Index Full Scan은 Ordered Row를 먼저 전달할 수 있습니다.
- Fast Full·Table Full + Sort는 전체 작업 후 첫 Row가 나올 수 있습니다.
- 첫 N행 목표와 전체 Fetch 목표를 분리하고 양쪽의 Fetch 범위를 동일하게 맞춥니다.
10세 Access Path를 동일 조건에서 검증하는 실행계획·Runtime 지표를 설명하시오.
검증 - Operation·Object·Partition, Starts·E/A-Rows, Buffers·Reads를 확인합니다. - Sort·TEMP·Table Access·Parallel과 Fetch 범위를 봅니다. - CPU·Elapsed·P95, Segment Block과 신규 Index DML·Redo·공간 회귀를 동일 Bind·환경에서 비교합니다.