이 글에 대한 내용은 SQL-SEVER DBMS에 관한 내용이다. 학습하기에 다른 DBMS와는 큰 차이가 있을 수 있다.
1. SQL server 기본 개념 : Page
📌 페이지(Page)란?
- 데이터는 테이블(Table) 형태로 저장됩니다.
- 테이블에는 여러 개의 레코드(Record) 또는 행(Row) 이 포함됩니다.
- 하지만 내부적으로는 데이터가 페이지(Page) 단위로 저장됩니다.
📌 페이지의 역할과 크기
- 하나의 테이블 데이터는 여러 개의 페이지에 나뉘어 저장될 수 있습니다.
- SQL Server에서 한 페이지의 크기는 8KB(킬로바이트) 이다.
- 1KB = 1024바이트
- 8KB = 8192바이트
📌 페이지 구조
페이지에는 여러 개의 요소가 포함됩니다.
- 페이지 헤더(Page Header)
- 시스템 정보를 저장하는 부분
- 이전 페이지와 다음 페이지 정보 저장
- 행 오프셋(Row Offset)
- 행(레코드)이 페이지의 어디에 위치하는지 저장
- 예: 첫 번째 행이 페이지의 어디에서 시작하는지, 두 번째 행은 어디에서 시작하는지 등
- 데이터 영역(Data Area)
- 실제 레코드(데이터)가 저장되는 영역
📌 데이터 저장 공간
- 총 페이지 크기: 8192 바이트 (8KB)
- 페이지 헤더 + 행 오프셋 크기: 132 바이트
- 실제 데이터 저장 가능 공간: 8060 바이트
한 페이지에는 최대 8060 바이트의 데이터(레코드)를 저장할 수 있음.
📌 정리
우리는 일반적으로 데이터가 테이블에 저장된다고 생각하지만, 실제로는 페이지 단위로 저장됨.
SQL Server에서 한 페이지의 크기는 8KB이며, 헤더와 오프셋을 제외하면 8060 바이트를 데이터 저장에 사용할 수 있음.
2. SQL server 기본 개념 : Extent
📌 익스텐트(Extent)란?
- 익스텐트(Extent) 는 8개의 페이지(Page) 로 구성된 더 큰 저장 단위이다.
- 1 익스텐트 = 8 페이지
- SQL Server에서 한 페이지 크기가 8KB 이므로, 1 익스텐트 크기는 8 × 8KB = 64KB 이다.
📌 익스텐트의 종류
SQL Server에서는 두 가지 유형의 익스텐트를 사용합니다.
- 혼합 익스텐트(Mixed Extent)
- 처음 8개 페이지(P1 ~ P8)는 혼합 익스텐트에 저장됩니다.
- 한 익스텐트 내에 여러 테이블의 페이지가 섞여 저장될 수 있음.
- 예:
- 같은 익스텐트에 학생 테이블(Student Table), 판매 테이블(Sales Table), 고객 테이블(Customer Table) 의 페이지가 함께 저장될 수 있음.
- 단일(고유) 익스텐트(Uniform Extent)
- 9번째 페이지(P9)부터는 전용 익스텐트(Uniform Extent) 할당.
- 하나의 익스텐트에 동일한 테이블의 데이터만 저장됨.
- 예:
- 학생 테이블의 데이터가 많아지면 P9부터는 학생 테이블 전용 익스텐트가 생성됨.
📌 SQL Server의 익스텐트 저장 방식
- SQL Server는 처음 8개의 페이지(P1 ~ P8)는 혼합 익스텐트(Mixed Extent)에 저장.
- P9부터는 단일 익스텐트(Uniform Extent)를 제공.
- Uniform Extent는 다른 테이블의 데이터를 포함하지 않으며, 오직 하나의 테이블 데이터만 포함.
📌 익스텐트 저장 방식 예시
1️⃣ 혼합 익스텐트 (P1 ~ P8)
익스텐트 번호 | 포함된 페이지 |
---|---|
익스텐트 1 | 학생 테이블 P1, 판매 테이블 P1, 고객 테이블 P1 등 |
익스텐트 2 | 학생 테이블 P2, 판매 테이블 P2, 고객 테이블 P2 등 |
2️⃣ 단일 익스텐트 (P9 이후)
익스텐트 번호 | 포함된 페이지 |
---|---|
익스텐트 3 | 학생 테이블 P9, P10, P11, P12, P13, P14, P15, P16 |
익스텐트 4 | 학생 테이블 P17, P18, P19, P20 (남은 공간은 비어 있음) |
📌 단일 익스텐트의 특징
- 특정 테이블의 데이터가 많아지면 새로운 익스텐트가 생성됨.
- 예를 들어, 학생 테이블에 새로운 데이터가 추가되면 P21이 생성되어 동일한 익스텐트에 추가.
- 다른 테이블의 데이터는 해당 익스텐트에 추가되지 않음.
📌 정리
- 1 익스텐트 = 8 페이지(총 64KB)
- P1 ~ P8은 혼합 익스텐트(Mixed Extent)
- P9부터는 단일 익스텐트(Uniform Extent), 같은 테이블의 데이터만 포함
- SQL Server는 처음 8개 페이지는 공유하지만, 이후부터는 테이블별 전용 익스텐트를 할당
SQL Server는 스토리지를 효율적으로 사용하면서도 성능을 최적화하기 위해 이러한 방식으로 데이터를 저장한다.
3. SQL Server 기본 개념 : Heap
📌힙 구조 : 클러스터형 인덱스(Clustered Index)가 없는 경우
- 테이블에 클러스터형 인덱스(Clustered Index)가 없으면 데이터는 힙(Heap) 형태로 저장된다.
- 힙(Heap) 구조란?
- 힙은 더미를 뜻함. 데이터가 정렬되지 않은 상태로 저장되어 있다.
- 특정 값을 검색하려면 모든 페이지를 스캔(Heap Scan)해야 함.
- SQL Server가 테이블 전체를 뒤져야 하므로 데이터 양이 많으면 검색 속도가 매우 느려짐.
- 알고리즘에서 완전 이진 탐색 트리의 최소 힙, 최대 힙 또는 컴퓨터 구조에서 스택/힙 영역에서의 힙과는 또 다른 의미로 사용되었다고 보아야 하겠다.
📌 힙(Heap) 구조에서 데이터 저장 방식
- 데이터는 혼합 익스텐트(Mixed Extent) → 단일 익스텐트(Uniform Extent) 순서로 저장됨.
- 예를 들어, 학생 테이블(Student Table) 에 20개의 페이지(P1~P20) 가 있다고 가정해 보겠습니다.
- P1~P8 → 혼합 익스텐트(Mixed Extent) 에 저장됨 (여러 테이블 데이터와 함께 저장).
- P9~P20 → 단일 익스텐트(Uniform Extent) 로 할당됨 (하나의 테이블 전용 저장).
📌 힙(Heap) 테이블에서 데이터 검색
예를 들어, 아래 SQL 쿼리를 실행한다고 가정해보자.
SELECT * FROM Student WHERE Name = 'Gary';
✅ SQL Server의 처리 과정
- 테이블의 모든 페이지를 하나씩 스캔(Heap Scan)하면서
"Gary"
를 찾음. - 예를 들어, P3에서
"Gary"
를 발견하고, P14에서도"Gary"
를 발견할 수 있음. - 하지만
"Gary"
를 찾기 위해 전체 20개의 페이지를 모두 읽어야 함. - 만약 테이블이 수백만 개의 페이지를 가진 대용량 데이터라면?
- SQL Server는 모든 페이지를 스캔해야 하므로 속도가 매우 느려짐.
- 데이터가 많을수록 성능이 저하됨.
📌 Heap Scan(힙 스캔) = Table Scan(테이블 전체 검색)
- SQL Server에서 힙 테이블의 데이터를 검색할 때 전체 테이블을 스캔하는 과정을 의미.
- 인덱스 없이 데이터를 조회하면, 모든 페이지를 하나하나 뒤져야 해서 성능이 저하됨.
📌 해결책: 클러스터형 인덱스(Clustered Index) 생성
- 힙(Heap) 테이블의 문제를 해결하려면 클러스터형 인덱스(Clustered Index)를 생성하면 됨.
- 클러스터형 인덱스를 생성하면?
- 기존의 무질서한 힙(Heap) 구조가 제거됨.
- 데이터가 정렬된 형태로 저장됨.
- 특정 값을 찾을 때 전체 테이블을 뒤질 필요 없이, 인덱스를 통해 빠르게 검색 가능.
📌 클러스터형 인덱스를 사용하면 SQL Server가 데이터를 효율적으로 검색할 수 있도록 구조를 최적화함
📌 정리
1️⃣ 클러스터형 인덱스가 없는 테이블(Heap Table)
- 데이터가 정렬되지 않고 무작위로 저장됨.
- 데이터를 검색할 때 모든 페이지를 스캔(Heap Scan = Table Scan) 해야 함.
- 데이터가 많을수록 성능이 매우 저하됨.
2️⃣ 클러스터형 인덱스를 생성하면?
- 기존 힙(Heap) 구조가 사라지고, 정렬된 데이터 구조로 저장됨.
- 특정 데이터를 찾을 때 전체 테이블을 스캔하지 않아도 됨 → 검색 속도가 훨씬 빨라짐.
4. 클러스터형 인덱스
📌 클러스터형 인덱스(Clustered Index)란?
- 클러스터형 인덱스를 생성하면 데이터가 자동으로 정렬된 상태로 저장됨.
- 정렬된 데이터는 B-트리(B-tree) 구조로 저장됨.
- 기존의 Heap 구조(무작위 저장 방식)는 제거되고, 정렬된 새로운 구조로 변경됨.
- 클러스터형 인덱스는 정렬된 테이블과 같음.
- B트리 구조로 정렬되어 있어서 O(log N)의 속도로 조회 가능.
📌 클러스터형 인덱스가 적용된 테이블 구조
✅ B-트리(B-tree) 구조의 개념
- 루트 노드(Root Node) → 최상위 노드
- 중간 노드(Intermediate Node) → 탐색을 위한 중간 단계
- 리프 노드(Leaf Node) → 실제 데이터가 저장되는 부분
✅ 클러스터형 인덱스 적용 예시
- 학생 테이블(Student Table) 이 있고, 다음과 같은 컬럼이 있다고 가정합니다.
ID
,Name
,Gender
,Blood Group
ID
컬럼에 클러스터형 인덱스를 생성하면?ID
컬럼을 기준으로 데이터가 자동 정렬됨.- B-트리 구조로 데이터를 저장하여 검색 속도를 빠르게 함.
📌 클러스터형 인덱스의 동작 방식
✅ ID 컬럼에 클러스터형 인덱스 적용 시
ID
기준으로 정렬된 상태에서 데이터가 저장됨.- SQL Server는 특정
ID
값을 찾을 때, 전체 데이터를 스캔하지 않고, B-트리 구조를 따라 빠르게 검색함. - 예를 들어, 다음 SQL 문을 실행한다고 가정하자.
SELECT * FROM Student WHERE ID = 5;
🔍 SQL Server 검색 과정
1️⃣ 루트 노드에서 ID = 5
가 포함된 방향(왼쪽/오른쪽)을 결정
2️⃣ 중간 노드를 따라 이동하며 범위를 좁힘
3️⃣ 리프 노드에서 ID = 5
를 빠르게 찾음
✅ 결과:
- 전체 테이블을 스캔할 필요 없이, 몇 번의 비교 연산만으로 원하는 데이터를 찾을 수 있음
- 검색 속도가 매우 빨라짐 (→ Index Seek)
📌 클러스터형 인덱스의 페이지 연결 구조
- B-트리(B-tree) 구조에서 페이지들은 서로 연결되어 있음.
- 루트 노드(Root Page) → 중간 노드(Intermediate Pages) → 리프 노드(Leaf Pages) 순서로 데이터가 저장됨.
- 각 페이지는 서로 연결(Interlinked) 되어 있어 양방향 탐색이 가능함.
✅ 페이지 간 이동 방식
1️⃣ 위에서 아래로 이동 가능 (탐색 과정)
- 루트 노드에서 중간 노드 → 리프 노드로 이동
- 예:
ID = 5
를 찾을 때- 루트 노드에서
5
가 포함된 방향(왼쪽/오른쪽)을 결정 - 중간 노드를 따라 이동하여
ID = 5
를 빠르게 찾음
- 루트 노드에서
2️⃣ 좌우로 이동 가능 (범위 검색)
- 리프 노드(Leaf Pages)끼리는 연결 리스트(Linked List)처럼 서로 연결됨
- 한 페이지에서 다음 페이지로 순차 이동 가능
- 예:
ID BETWEEN 3 AND 7
같은 범위 검색에서 유용함
3️⃣ 아래에서 위로 이동 가능 (트리 구조 유지)
- 데이터 변경이 발생하면 루트, 중간, 리프 노드 간 연결이 유지됨
- 인덱스 구조를 재조정하면서 검색 성능을 유지
✅ 클러스터형 인덱스의 B-트리 구조에서는 페이지들이 서로 연결되어 있어, 빠르게 데이터를 찾고 이동할 수 있음
📌 Index Seek vs Index Scan
✅ Index Seek (빠른 검색)
- 클러스터형 인덱스가 적용된 컬럼을 사용하여 검색하면 Index Seek 발생
- B-트리 구조를 따라 필요한 데이터만 빠르게 검색
- ✅ 속도가 빠름 (로그arithm 연산 O(log n))
✅ Index Scan (전체 스캔)
- 클러스터형 인덱스가 적용되지 않은 컬럼을 기준으로 검색하면 Index Scan 발생
- 예를 들어,
Name
컬럼에 대한 검색을 실행할 경우
SELECT * FROM Student WHERE Name = 'Gary';
- 이 경우 클러스터형 인덱스가 ID 기준으로 정렬되어 있으므로, Name을 기준으로 정렬되어 있지 않음
- SQL Server는 전체 데이터를 스캔해야 함 → 검색 속도가 느려짐
- 하지만, Index Scan은 힙의 Full Scan(Table Scan)과는 다름
✅ 결과:
- Index Seek → 정렬된 컬럼(
ID
)을 이용한 검색 (빠름) - Index Scan → 정렬되지 않은 컬럼(
Name
)을 이용한 검색 (느림)
검색 방식 | 시간 복잡도 | 검색 방식 |
---|---|---|
Index Seek | O(log n) | B-트리 탐색 (빠름) |
Index Scan | O(n) | 전체 인덱스를 스캔 (느림) |
Table Scan (Heap Scan) | O(n) | 전체 테이블을 스캔 (가장 느림) |
📌 INDEX SEEK : O(log n)
📍 예를 들어, 100만 개(1,000,000) 데이터가 있을 때:
- B-트리는 균형 트리(Balanced Tree)이므로, 높이(log₂ n)도 낮음.
- log₂(1,000,000) ≈ 20
→ 최악의 경우에도 20번 이하의 비교만으로 원하는 데이터를 찾을 수 있음. - 만약 데이터가 10억 개(1,000,000,000)라면?
- log₂(1,000,000,000) ≈ 30
- 30번 이하의 비교 연산만으로 검색 가능
📌 클러스터형 인덱스의 한계점
- 클러스터형 인덱스는 하나의 컬럼에만 적용 가능 (보통 기본키)
- 다른 컬럼을 기준으로 검색할 경우 Index Scan이 발생하여 성능 저하 가능성 있음
- 해결책: 비클러스터형 인덱스(Non-Clustered Index) 활용
Name
컬럼을 기준으로 빠르게 검색하려면, 비클러스터형 인덱스를 생성해야 함.
📌 정리
✅ 클러스터형 인덱스란?
- 데이터를 자동 정렬하여 저장하는 방식
- B-트리 구조를 사용해 검색 속도를 빠르게 최적화
- 특정 컬럼(보통
ID
)을 기준으로 빠른 검색 가능 (Index Seek 발생) - 하지만 정렬되지 않은 컬럼을 검색하면 Index Scan 발생 → 속도 저하 가능성
✅ Index Seek vs Index Scan
- Index Seek: 빠른 검색 (B-트리 활용)
- Index Scan: 느린 검색 (전체 테이블 스캔)
5. 비클러스터형 인덱스
📌 비클러스터형 인덱스(Non-Clustered Index)란?
- 클러스터형 인덱스(Clustered Index)와는 별도로 추가적으로 생성되는 인덱스.
- 데이터 정렬 방식이 다름:
- 클러스터형 인덱스는 기본키(예: ID)를 기준으로 정렬됨.
- 비클러스터형 인덱스는 특정 컬럼(예: Name) 기준으로 별도의 B-트리 구조를 가짐.
- 기존 테이블과는 별개로 추가적인 정렬된 구조를 생성하는 것이 특징
📌 비클러스터형 인덱스 생성 예제
✅ 기본 테이블 구조
Student Table (학생 테이블)
예제:ID
(기본키) → 클러스터형 인덱스 적용됨Name
,Gender
,Blood Group
등의 컬럼 포함
✅ 비클러스터형 인덱스 적용 과정
1️⃣ 기존 클러스터형 인덱스: ID 컬럼을 기준으로 정렬됨
2️⃣ 비클러스터형 인덱스를 Name 컬럼에 생성
- 새로운 B-트리 구조가 추가 생성됨
- Name 컬럼을 기준으로 알파벳 순서로 정렬됨
📌 **비클러스터형 인덱스를 생성하면 Name 컬럼을 기준으로 정렬된 별도의 인덱스 구조가 추가됨.
📌 비클러스터형(Non-Clustered) 인덱스의 구조
비클러스터형 인덱스를 생성하면 새로운 B-트리 구조가 생성되며, 리프 노드에는 정렬된 인덱스 컬럼 값과 해당 데이터의 위치(클러스터형 인덱스의 키 값) 가 저장된다.
다시 말해, 비클러스터형 인덱스는 데이터 자체를 정렬하지 않고, 인덱스만 정렬해서 빠르게 찾을 수 있도록 도와주는 역할을 함.
✅ "비클러스터형"이라는 이름이 붙은 이유는 인덱스와 실제 데이터가 따로 저장되기 때문.
📌 예제: 비클러스터형 인덱스와 데이터 저장 방식
✅ 학생 테이블 (Student Table)
ID (클러스터형 인덱스) | Name | Gender |
---|---|---|
1 | Bob | M |
2 | Gary | M |
3 | Ellen | F |
4 | Holly | F |
5 | Gary | M |
✅ 이제 Name 컬럼에 비클러스터형 인덱스를 생성하면?
비클러스터형 인덱스는 B-트리 구조에서 Name 컬럼을 정렬하지만, 실제 데이터는 그대로 남아 있음.
Name | ID (클러스터형 인덱스) |
---|---|
Bob | 1 |
Ellen | 3 |
Gary | 2, 5 |
Holly | 4 |
📌 이제 "Gary"를 검색하면?
1️⃣ 비클러스터형 인덱스를 먼저 탐색하여 "Gary"가 있는 위치(= ID 값 2, 5)를 찾음
2️⃣ ID 값을 기반으로 클러스터형 인덱스를 탐색하여 실제 데이터를 가져옴
✅ 비클러스터형 인덱스는 데이터를 직접 포함하지 않고, 인덱스만 정렬하여 검색을 빠르게 도와주는 역할을 하기 때문에 "비(非)클러스터형"이라고 부름
📌 비클러스터형 인덱스를 활용한 검색
✅ 쿼리 실행 예제
SELECT * FROM Student WHERE Name = 'Gary';
- SQL Server는 비클러스터형 인덱스에서 Name 컬럼을 검색
- B-트리 구조에서 "Gary" 를 찾아 해당 ID 값(2, 5)을 가져옴
- 이후 ID 값(2, 5)을 이용해 클러스터형 인덱스를 탐색하여 전체 레코드를 가져옴
📌 비클러스터형 인덱스를 활용하면 전체 테이블을 스캔하지 않고도 빠르게 데이터에 접근 가능
📌 인덱스 탐색 과정 (Index Seek)
✅ 쿼리 실행 과정
1️⃣ 비클러스터형 인덱스 탐색
- "Gary" 가 포함된 페이지를 탐색 (Index Seek 발생)
- 해당하는 ID 값(2, 5) 추출
2️⃣ 클러스터형 인덱스 탐색
- 추출된 ID(2, 5)를 이용해 클러스터형 인덱스에서 실제 데이터를 찾음 (Index Seek 발생)
✅ 결과:
- 전체 데이터를 스캔할 필요 없이 몇 번의 탐색(Seek)만으로 원하는 데이터를 가져올 수 있음
- 수백만 개의 데이터가 있어도 성능 저하 없이 빠르게 검색 가능
📌 Index Seek vs Index Scan
검색 방식 | 시간 복잡도 | 동작 방식 |
---|---|---|
Index Seek | O(log n) | B-트리 탐색 (빠름) |
Index Scan | O(n) | 인덱스를 전체 스캔 (느림) |
Table Scan (Heap Scan) | O(n) | 테이블 전체 스캔 (가장 느림) |
✅ 비클러스터형 인덱스를 활용하면 Index Seek을 통해 빠른 검색이 가능
📌 정리
✔ 비클러스터형 인덱스란?
- 클러스터형 인덱스와 별개로 추가 생성되는 인덱스
- 특정 컬럼(예: Name)에 대한 별도의 B-트리 구조를 생성하여 데이터 검색 속도 향상
✔ 비클러스터형 인덱스의 장점
- 데이터 정렬 방식이 다름 (기존 테이블 구조 변경 없음)
- 특정 컬럼 기준으로 검색 시 Index Scan이 아닌 Index Seek을 사용하여 빠르게 데이터 찾기 가능
- 수백만 개의 데이터가 있어도 빠른 검색 성능 유지 가능
📌 비클러스터형 인덱스를 활용하면 특정 컬럼을 기준으로 Index Seek만을 사용하여 빠르게 검색할 수 있어 SQL 성능이 최적화됨
6. 클러스터 / 비클러스터란?
📌 "클러스터(Cluster)"란 무엇인가?
"클러스터(Cluster)"라는 단어는 "무리를 이루다" 또는 "한곳에 모여 있다"는 의미를 가진다.
비슷한 성질을 가진 데이터나 객체들이 물리적으로 가까이 모여 있는 상태를 의미한다.
✅ 데이터베이스에서 "클러스터(Cluster)"의 의미
- 데이터베이스 클러스터(DB Cluster): 데이터를 논리적으로 또는 물리적으로 한데 모아 정렬하여 저장하는 방식
✅ 클러스터형 인덱스(Clustered Index)란?
- 데이터를 물리적으로 정렬된 상태로 저장하여, 하나의 무리(Cluster)로 관리하는 방식
📌 클러스터형 인덱스(Clustered Index)란?
"클러스터형(Clustered)"이라는 용어가 붙은 이유는, 데이터 자체가 정렬된 형태로 저장되며, 물리적으로도 정렬된 상태를 유지하기 때문이다.
클러스터형 인덱스는 데이터를 정렬된 형태로 묶어서(Cluster) 저장하는 구조를 의미한다.
📌 특징:
✔ 데이터 자체가 인덱스의 순서에 맞게 정렬된 상태로 저장됨
✔ B-트리(B-tree) 구조의 리프 노드(Leaf Node)에는 실제 데이터가 저장됨
✔ 검색 시 인덱스를 따라가면 바로 정렬된 데이터를 가져올 수 있어 빠름
✅ 클러스터형 인덱스는 데이터를 논리적 정렬뿐만 아니라 물리적으로도 정렬된 상태로 유지하기 때문에 "클러스터(Clustered)"라는 명칭이 붙음
📌 비클러스터형 인덱스(Non-Clustered Index)란?
"비클러스터형(Non-Clustered)"이라고 불리는 이유는, 데이터 자체가 정렬되지 않고, 인덱스만 별도로 정렬되기 때문이다.
인덱스와 실제 데이터가 분리된 구조를 가지며, 인덱스를 통해 데이터의 위치를 찾아야 한다.
📌 특징:
✔ 인덱스는 정렬되지만, 실제 데이터는 원래 위치에 남아 있음
✔ 리프 노드(Leaf Node)에는 실제 데이터가 아니라, 데이터의 위치(예: 클러스터형 인덱스의 기본키)가 저장됨
✔ 검색 시, 먼저 인덱스를 탐색한 후, 해당 데이터를 다시 찾아야 함
✅ 비클러스터형 인덱스는 데이터를 직접 포함하지 않고, 참조만 하기 때문에 "비(非)클러스터형(Non-Clustered)"이라고 부름
📌 클러스터형 vs 비클러스터형 인덱스 비교
구분 | 클러스터형 인덱스(Clustered Index) | 비클러스터형 인덱스(Non-Clustered Index) |
---|---|---|
클러스터(Cluster) 개념 | 데이터가 물리적으로 정렬된 상태로 저장됨 | 인덱스만 정렬되고, 데이터는 원래 위치에 남아 있음 |
데이터 저장 위치 | B-트리의 리프 노드에 실제 데이터가 저장됨 | B-트리의 리프 노드에는 데이터의 위치(참조값)만 저장됨 |
검색 과정 | 바로 데이터에 접근 가능 (빠름, O(log n)) | 먼저 인덱스를 검색한 후, 데이터를 다시 찾아야 함 |
테이블당 개수 | 하나의 테이블당 1개만 생성 가능 | 여러 개 생성 가능 (여러 컬럼에 인덱스 적용 가능) |
✅ 클러스터형 인덱스는 데이터를 하나의 클러스터(정렬된 형태)로 저장하기 때문에 "클러스터형"이라는 이름이 붙었고, 비클러스터형 인덱스는 데이터가 따로 존재하기 때문에 "비클러스터형"이라는 이름이 붙음
📌 정리
✔ "클러스터(Cluster)"란?
→ 같은 성질의 데이터가 물리적으로 모여 있는 상태
✔ 클러스터형 인덱스(Clustered Index)
→ 데이터를 물리적으로 정렬된 상태로 저장하는 방식
✔ 비클러스터형 인덱스(Non-Clustered Index)
→ 인덱스는 정렬되지만, 실제 데이터는 원래 위치에 남아 있음
✅ 클러스터형 인덱스는 데이터를 정렬된 상태로 하나의 클러스터(Cluster)처럼 관리하기 때문에 붙여진 이름
7. 클러스터형 인덱스 없이 비클러스터형 인덱스만 존재하는 경우
📌 클러스터형 인덱스 없이 비클러스터형 인덱스만 존재하는 경우
- 테이블에 클러스터형 인덱스가 없으면 데이터는 힙(Heap) 형태로 저장됨.
- 비클러스터형 인덱스만 존재하고, 클러스터형 인덱스가 없으면 데이터 정렬 없이 무작위로 저장됨.
- 비클러스터형 인덱스는 데이터의 위치를 클러스터형 인덱스의 키 값이 아니라 "주소(Address)"를 통해 참조함.
✅ 클러스터형 인덱스가 없으면, 비클러스터형 인덱스는 데이터의 실제 위치(메모리 주소)를 저장하는 방식으로 동작
📌 비클러스터형 인덱스가 주소를 저장하는 방식
✅ 기본 테이블 구조 예시
Student Table (학생 테이블)
이 있고, 다음과 같은 컬럼이 있다고 가정ID
(클러스터형 인덱스 없음 → 데이터는 정렬되지 않은 힙에 저장)Name
,Gender
,Blood Group
등의 컬럼 포함
- Name 컬럼에 비클러스터형 인덱스를 생성하면?
- B-트리 구조에서 Name 컬럼을 기준으로 정렬된 인덱스가 생성됨
- 하지만 클러스터형 인덱스가 없으므로, ID 값을 저장하는 대신 "데이터의 메모리 주소(Address)"를 저장함
📌 비클러스터형 인덱스의 구조 (클러스터형 인덱스 없음)
Name | 주소(Address) |
---|---|
Alan | 1024 (Page 2) |
Bob | 2048 (Page 5) |
Gary | 3072 (Page 6) |
Gary | 4096 (Page 16) |
Holly | 5120 (Page 7) |
✅ 클러스터형 인덱스가 없는 경우 비클러스터형 인덱스는 "주소"를 저장하여 해당 데이터가 있는 위치를 직접 참조함
📌 비클러스터형 인덱스를 활용한 검색 과정 (클러스터형 인덱스 없음)
✅ 쿼리 실행 예제
SELECT * FROM Student WHERE Name = 'Gary';
✅ SQL Server의 검색 과정
1️⃣ 비클러스터형 인덱스에서 "Gary"를 찾음
- B-트리 탐색을 수행하여 "Gary"가 포함된 노드를 찾음 (Index Seek 발생)
- Gary의 주소 값(3072, 4096)을 가져옴
2️⃣ 주소 값을 이용해 실제 데이터 위치로 이동
- 주소
3072
를 참조하여 Page 6으로 이동 → 첫 번째 Gary 데이터 가져옴 - 주소
4096
을 참조하여 Page 16으로 이동 → 두 번째 Gary 데이터 가져옴
✅ 클러스터형 인덱스가 없을 때는, 비클러스터형 인덱스가 "데이터의 주소"를 저장하고 해당 위치를 직접 참조하는 방식으로 동작
📌 클러스터형 인덱스가 있을 때 vs 없을 때
구분 | 클러스터형 인덱스가 있는 경우 | 클러스터형 인덱스가 없는 경우 |
---|---|---|
비클러스터형 인덱스의 저장 값 | 클러스터형 인덱스의 키 값 (예: ID) | 데이터의 메모리 주소 (예: Page 6, Page 16) |
데이터 저장 방식 | B-트리 + 정렬된 상태로 저장 | 정렬되지 않은 힙(Heap)에 저장 |
검색 과정 | 비클러스터형 인덱스 → 클러스터형 인덱스 탐색 → 데이터 검색 | 비클러스터형 인덱스 → 주소 참조 → 데이터 검색 |
✅ 클러스터형 인덱스가 없을 때는, 비클러스터형 인덱스가 데이터를 빠르게 찾기 위해 "주소"를 저장하고 참조하는 방식으로 동작함
📌 정리
✔ 클러스터형 인덱스가 없으면 데이터는 힙(Heap) 형태로 저장됨
✔ 비클러스터형 인덱스는 데이터를 찾기 위해 "메모리 주소(Address)"를 저장하여 참조
✔ 검색 시 비클러스터형 인덱스를 통해 주소를 찾고, 해당 주소를 참조하여 데이터를 가져옴
✔ 클러스터형 인덱스가 있을 때보다 한 단계 더 참조해야 하지만, 그래도 테이블 전체를 스캔하는 것보다 훨씬 빠름
8. 인덱스의 깊이
📌 인덱스는 깊지 않다(Index Depth is Not Very Deep)
- B-트리(B-tree) 구조에서 인덱스는 단순히 두 개의 브랜치(branch) 만 가지는 것이 아님.
- 실제로는 페이지 크기(8KB)가 크기 때문에, 한 페이지에 많은 데이터를 저장할 수 있음.
- B-트리의 각 노드 하나가 하나의 페이지라고 생각하면 됨.
- 두 개의 노드만 있는 것이 아니라(log2n), 한 번에 여러 개의 자식 노드(Branches)가 생성될 수 있음(logmn).
- 그 결과, 인덱스의 깊이가 깊지 않으며, 검색 속도가 빠름.
✅ 인덱스는 깊고 복잡한 구조가 아니라, 넓게 분기(logm n)하여 검색 속도를 최적화하는 구조
📌 B-트리(B-tree)에서 인덱스의 분기(branching) 방식
- 8KB 페이지에는 단 2개의 값만 저장되는 것이 아니라, 훨씬 더 많은 값을 저장 가능.
- 예를 들어, 클러스터형 인덱스를 ID 컬럼에 생성한다고 가정.
📌 데이터 예제:
ID (클러스터형 인덱스) | Name | Gender | Blood Group |
---|---|---|---|
1 | Alan | M | O+ |
10,000 | Bob | M | B+ |
20,000 | Gary | M | AB- |
30,000 | Holly | F | A+ |
✅ 루트 노드(Root Node)
- 루트 노드에 1 ~ 1,000,000 데이터가 저장될 수 있음
- 예를 들어,
- Branch 1:
ID 1 ~ 10,000
- Branch 2:
ID 10,000 ~ 20,000
- Branch 3:
ID 20,000 ~ 30,000
- Branch 4:
ID 30,000 ~ 40,000
- Branch 1:
✅ 중간 노드(Intermediate Nodes)
- 각 브랜치는 더 작은 범위로 세분화됨
- 예를 들어,
ID 1 ~ 10,000
→ 100개~200개 단위로 더 작은 노드로 분할ID 10,000 ~ 20,000
→ 또 다른 브랜치로 분할
✅ 리프 노드(Leaf Nodes)
- 실제 데이터가 저장되는 곳
- 예를 들어,
ID 1 ~ 100
→ 한 페이지에 저장ID 100 ~ 200
→ 다른 페이지에 저장ID 200 ~ 300
→ 다른 페이지에 저장
✅ 데이터를 여러 개의 브랜치로 나누어 저장하기 때문에, 인덱스의 깊이가 깊지 않으며, 검색 속도가 빠름
📌 B-트리의 깊이가 깊어지지 않는 이유
- 루트 노드 → 중간 노드 → 리프 노드 순서로 탐색하면서 빠르게 검색 가능
- 각 페이지(8KB)에 많은 데이터가 저장될 수 있기 때문에, 브랜치가 많아져 깊이가 깊어질 필요 없음
- 예를 들어,
- 각 노드가 100개 이상의 값(레코드)을 저장할 수 있다면, 1,000,000개의 데이터를 검색할 때도 단 3~4단계 정도만 거치면 찾을 수 있음
✅ 인덱스가 깊지 않고, 넓게 퍼지면서 빠르게 데이터를 찾을 수 있는 구조
📌 정리
✔ B-트리에서 인덱스는 깊지 않고, 넓게 분기되는 구조
✔ 8KB 페이지가 많은 데이터를 저장할 수 있기 때문에, 두 개의 브랜치만 존재하는 것이 아님
✔ 각 페이지에 많은 값을 저장할 수 있어, 깊이가 깊어질 필요 없이 빠르게 검색 가능
✔ 인덱스 탐색 시, O(log n)의 속도로 빠르게 원하는 데이터를 찾을 수 있음
✅ 인덱스가 깊지 않고, 넓게 퍼져서 빠르게 검색이 가능하다는 점이 핵심
9. 복합 인덱스
📌 복합 인덱스(Composite Index)란?
- 하나의 인덱스를 두 개 이상의 컬럼에 적용하는 방식.
- 단일 컬럼이 아닌 여러 컬럼을 기준으로 정렬된 인덱스가 생성됨.
- 비클러스터형 인덱스(Non-Clustered Index)와 함께 사용되며, 여러 컬럼을 동시에 검색할 때 성능을 향상시킴.
✅ 복합 인덱스는 단일 컬럼 인덱스보다 더 많은 검색 조건을 최적화할 수 있음
📌 복합 인덱스의 구조 및 정렬 방식
복합 인덱스를 Last Name(성)
+ First Name(이름)
컬럼에 생성한다고 가정
Last Name | First Name | ID (클러스터형 인덱스 키) |
---|---|---|
Arora | Amit | 71 |
Arora | Bharti | 22 |
Arora | Rahul | 5 |
Batra | Ajay | 18 |
Batra | Deepak | 35 |
Chandra | Pooja | 11 |
✅ 정렬 방식:
1️⃣ Last Name(성) 기준으로 먼저 정렬
2️⃣ 동일한 Last Name 내에서 First Name(이름) 기준으로 정렬
3️⃣ 각 행은 ID 값을 포함하여 클러스터형 인덱스를 활용
📌 "Arora"가 먼저 나오고, "Arora" 내에서 "Amit → Bharti → Rahul" 순으로 정렬됨
📌 복합 인덱스를 활용한 검색 과정
✅ 쿼리 실행 예제
SELECT * FROM Employee WHERE LastName = 'Arora' AND FirstName = 'Rahul';
✅ SQL Server의 검색 과정
1️⃣ B-트리를 통해 "Arora Rahul"을 찾음 (Index Seek 발생)
2️⃣ 해당하는 ID 값(5)을 가져옴
3️⃣ ID를 사용하여 클러스터형 인덱스에서 실제 데이터를 가져옴
✅ 복합 인덱스를 사용하면 두 개의 컬럼을 동시에 활용하여 빠르게 검색 가능
📌 복합 인덱스가 활용되는 경우
✅ 인덱스가 효과적으로 사용되는 경우
📌 복합 인덱스가 (Last Name, First Name) 순서로 생성되었을 때
검색 조건 | 인덱스 사용 여부 | 설명 |
---|---|---|
WHERE LastName = 'Batra' AND FirstName = 'Deepak' |
✅ 사용 가능 | LastName, FirstName 순서대로 검색 |
WHERE LastName = 'Batra' |
✅ 사용 가능 | LastName 단독 검색도 가능 |
WHERE LastName = 'Arora' AND FirstName LIKE 'R%' |
✅ 사용 가능 | 인덱스가 LastName → FirstName 순서이므로 범위 검색 가능 |
WHERE LastName LIKE 'A%' |
✅ 사용 가능 | LastName이 먼저 정렬되었으므로 범위 검색 가능 |
WHERE FirstName = 'Amit' |
❌ 사용 불가 | Last Name 없이 First Name만 검색하면 인덱스를 활용할 수 없음 |
✅ 복합 인덱스는 첫 번째 컬럼(Last Name)부터 차례로 검색 조건에 포함될 때만 최적의 성능을 발휘함
📌 복합 인덱스의 컬럼 순서가 중요한 이유
✅ (Last Name, First Name) vs (First Name, Last Name) 비교
📌 (Last Name, First Name) 순서로 생성된 인덱스
Last Name | First Name |
---|---|
Arora | Amit |
Arora | Bharti |
Arora | Rahul |
Batra | Ajay |
Batra | Deepak |
📌 (First Name, Last Name) 순서로 생성된 인덱스
First Name | Last Name |
---|---|
Amit | Arora |
Amit | Singh |
Amit | Tandon |
Deepak | Batra |
Rahul | Arora |
✅ 컬럼 순서가 다르면 정렬 방식도 달라지므로, 검색 최적화가 완전히 달라짐
📌 정리
✔ 복합 인덱스(Composite Index)란?
→ 두 개 이상의 컬럼에 대해 정렬된 인덱스를 생성하는 방식
✔ 복합 인덱스의 정렬 방식
→ 첫 번째 컬럼(Last Name) 기준으로 먼저 정렬, 두 번째 컬럼(First Name) 기준으로 정렬
✔ 복합 인덱스를 활용한 검색
→ 첫 번째 컬럼부터 차례로 검색 조건에 포함될 때 최적의 성능 발휘
✔ 컬럼 순서가 중요한 이유
→ (Last Name, First Name)과 (First Name, Last Name)은 서로 다른 정렬 구조를 가지므로, 인덱스 검색 방식이 달라짐
✔ 신중하게 컬럼 순서를 선택한다는 의미
→ 테이블을 정의할 때 컬럼 순서, where절로 작성된 조건의 순서와는 관련이 없고, 복합 인덱스를 거는 순서만 관련이 있음. WHERE 필터 조건의 순서는 SQL 옵티마이저가 내부적으로 재정렬하므로 성능에 직접적인 영향을 주지 않는다.
✅ 복합 인덱스를 사용할 때는 컬럼 순서를 신중하게 선택해야 성능 최적화 가능
10. 복합인덱스의 좋은 활용 예시
📌 특정 정렬 방식이 자주 사용될 경우, 복합 인덱스를 활용할 수 있음
- 특정 쿼리에서 자주 사용되는 정렬 순서를 고려하여 복합 인덱스를 생성하면 성능을 최적화할 수 있음.
- 예를 들어, 주문 날짜(Order Date)는 내림차순(Descending), 고객 이름(Name)은 오름차순(Ascending)으로 정렬된 결과를 자주 조회한다고 가정.
✅ 이런 경우, 복합 인덱스를 활용하면 SQL Server가 불필요한 정렬 연산을 수행하지 않아도 됨
📌 원하는 출력 예시
📌 주문 날짜(Order Date)는 내림차순(최근 날짜부터), 고객 이름(Name)은 오름차순(A~Z 정렬)
Order Date (DESC) | Name (ASC) |
---|---|
2023-10-23 | Alice |
2023-10-23 | Bob |
2023-10-23 | Charlie |
2023-10-22 | Alan |
2023-10-22 | David |
2023-10-22 | Emily |
✅ 최근 주문일(Order Date)부터 정렬되며, 같은 날짜 내에서는 고객 이름(Name)이 알파벳 순으로 정렬됨
📌 복합 인덱스 적용 방법
CREATE INDEX idx_order_name ON Orders (OrderDate DESC, Name ASC);
OrderDate DESC
→ 내림차순 정렬Name ASC
→ 오름차순 정렬
✅ 이렇게 복합 인덱스를 생성하면 데이터가 미리 정렬된 상태로 저장됨
📌 SQL Server에서 단순/복합 인덱스를 사용한 정렬 최적화
✅ 쿼리 실행 예제
SELECT * FROM Orders ORDER BY OrderDate DESC, Name ASC;
✅ 일반적으로 이 쿼리를 실행하면, SQL Server가 데이터를 가져온 후 별도로 정렬을 수행해야 함
✅ 하지만 복합 인덱스가 존재하면, 데이터가 이미 정렬된 상태이므로 추가적인 정렬 작업이 필요 없음
단순 인덱스 또한 정렬된 순서대로 데이터를 이미 가지고 있기 때문에 SQL Server가 별도로 정렬 작업을 하지 않아도 되고, ORDER BY 연산을 최적화한다.
단순/복합 인덱스를 활용하면 ORDER BY 연산을 최적화하여 성능을 향상시킬 수 있음
📌 복합 인덱스가 중요한 이유
✔ 단순/복합 인덱스는 ORDER BY 연산을 최적화하여 성능 향상 가능함을 잊지 말자
✔ SQL Server가 별도로 정렬할 필요 없이, 이미 정렬된 상태에서 데이터를 가져옴
✔ 대량 데이터에서 정렬이 자주 발생하면, 인덱스를 활용하여 부하를 줄일 수 있음
✅ ORDER BY 연산이 자주 사용되는 쿼리라면, 복합 인덱스를 활용하여 정렬 비용을 줄일 수 있음
11. 인덱스 조각화
📌 인덱스 조각화(Index Fragmentation)란?
- 인덱스가 생성될 때, 데이터는 정렬된 순서대로 저장됨
- 하지만 데이터 삽입/수정/삭제가 발생하면서 기존의 정렬된 데이터가 깨지고, 새로운 페이지가 추가됨
- 논리적 순서(Logical Order)와 물리적 순서(Physical Order)가 일치하지 않는 상태를 "조각화(Fragmentation)"라고 함
- 조각화가 심해지면 쿼리 성능이 저하됨
✅ 조각화는 데이터가 정렬된 순서대로 유지되지 못하고, 새로운 페이지가 삽입되면서 발생하는 문제이다.
📌 인덱스 조각화 발생 과정
✅ 초기 상태 (정상적인 페이지 배치)
- 페이지(Page) 당 4개의 레코드가 저장됨 (단순화된 예제)
- 데이터가 정렬된 순서대로 저장됨
📌 초기 페이지 상태
Page 1 | Page 2 | Page 3 |
---|---|---|
1, 3, 5, 7 | 8,9,10,11 | 12, 13, 14, 15 |
✅ 이 상태에서는 인덱스가 잘 정렬되어 있고, SQL Server가 데이터를 빠르게 검색 가능
✅ 데이터 삽입 시 조각화 발생
- 새로운 데이터(
2, 4, 6
)를 삽입하면, 기존의 정렬 순서를 유지해야 함 - 하지만 페이지가 가득 차 있는 경우, 데이터를 이동시키는 과정에서 새로운 페이지가 생성됨
📌 데이터 삽입 후 변경된 페이지 상태 (조각화 발생)
Page 1 | Page 4 (Out of Sequence) | Page 2 | Page 3 |
---|---|---|---|
1, 2, 3, 4 | 5, 6, 7 | 8,9,10,11 | 12, 13, 14, 15 |
✅ 새로운 페이지(Page 4)가 기존 순서에서 벗어나, 논리적 순서와 물리적 순서가 일치하지 않음 → 조각화 발생
📌 논리적 순서(Logical Order) vs 물리적 순서(Physical Order)
- 논리적 순서: 우리가 데이터를 정렬된 상태로 인식하는 방식
- 물리적 순서: 실제 하드디스크나 SSD에 저장된 데이터의 위치
✅ 조각화가 발생하면, 논리적 순서대로 데이터를 찾으려 해도 물리적으로 떨어져 있는 데이터를 찾아가야 하기 때문에 검색 속도가 저하됨
📌 SQL Server에서 인덱스 조각화가 성능 저하를 일으키는 이유
1️⃣ 페이지가 연속적으로 배치되지 않아서 "읽기(Read) 작업"이 많아짐
2️⃣ 하드디스크의 Read/Write Head가 데이터를 찾기 위해 여러 곳을 이동해야 함 (랜덤 액세스 증가)
3️⃣ 범위 검색(Range Scan) 시, 데이터를 연속적으로 읽지 못하고, 여러 위치를 왔다 갔다 하며 읽어야 함
✅ 조각화가 많아지면 데이터 검색 속도가 느려지고, SQL Server의 성능이 저하됨
📌 인덱스 조각화 해결 방법
SQL Server에서는 조각화를 해결하기 위해 두 가지 방법을 사용
✅ 1. 인덱스 재구성(Index Reorganize)
ALTER INDEX idx_name ON table_name REORGANIZE;
- 기존 인덱스를 그대로 유지하면서, 페이지 간의 순서를 재정렬
- 적은 리소스를 사용하여 인덱스의 조각화를 줄이는 방법
- 조각화가 심하지 않을 때(10~30%) 적절한 방법
✅ 2. 인덱스 재구축(Index Rebuild)
ALTER INDEX idx_name ON table_name REBUILD;
- 인덱스를 삭제하고, 새로운 인덱스를 생성
- 완전히 새로운 정렬된 구조로 인덱스를 다시 만듦 → 성능 최적화
- 조각화가 심할 때(30% 이상) 추천되는 방법
✅ 조각화가 심해지면 "인덱스 재구성" 또는 "인덱스 재구축"을 수행하여 성능을 최적화할 수 있음
📌 정리
✔ 인덱스 조각화(Index Fragmentation)란?
→ 데이터 삽입/수정/삭제로 인해 논리적 순서와 물리적 순서가 일치하지 않는 현상
✔ 조각화가 심해지면 성능 저하 발생
→ 랜덤 액세스 증가, 데이터 검색 속도 저하, 범위 검색 성능 저하
✔ 조각화 해결 방법
- 인덱스 재구성 (REORGANIZE): 기존 인덱스 유지, 가벼운 최적화
- 인덱스 재구축 (REBUILD): 새로운 인덱스 생성, 강력한 최적화
✅ 조각화가 심해지면 인덱스를 정리하여 성능을 최적화해야 함
12. 인덱스 조각화의 원인
📌 인덱스 조각화(Fragmentation)의 원인
조각화는 단순히 데이터 삽입(INSERT) 뿐만 아니라, 업데이트(UPDATE)와 삭제(DELETE) 작업에서도 발생할 수 있음.
✅ 데이터가 변경될 때마다 인덱스가 물리적으로 정렬된 상태를 유지하기 어려워지고, 이로 인해 조각화가 발생함
📌 1. 데이터 삽입(INSERT)에 의한 조각화
📌 데이터를 삽입할 때 조각화가 발생하는 원리
- 클러스터형 인덱스(Clustered Index) 가 존재하는 경우, 데이터는 항상 정렬된 상태로 유지됨
- 따라서 기존 데이터 사이에 새로운 데이터가 삽입되면, 데이터를 재배열해야 하는 문제가 발생함
- 만약 페이지가 이미 가득 찼다면, 새로운 데이터를 위한 "새로운 페이지(Out of Sequence Page)"가 생성됨
✅ 이로 인해 논리적 순서와 물리적 순서가 달라지면서 조각화(Fragmentation)가 발생함
📌 2. 데이터 업데이트(UPDATE)에 의한 조각화
📌 업데이트로 인해 조각화가 발생하는 원리
- 데이터의 크기가 업데이트 후 증가하면, 기존 페이지에 데이터를 유지할 공간이 부족할 수 있음
- 이 경우, SQL Server는 데이터를 새로운 페이지로 이동시키면서 조각화가 발생함
✅ 업데이트로 인해 데이터가 커질 경우, 기존 페이지에 남아 있을 공간이 부족하면 새로운 페이지가 생성되어 조각화가 발생함
✅ 예제: 업데이트로 조각화가 발생하는 과정
1️⃣ 초기 상태 (정렬된 데이터)
Page 1 | Page 2 |
---|---|
R1, R2, R3, R4 | R5, R6, R7, R8 |
2️⃣ R1을 업데이트하여 크기가 커짐 → 기존 페이지에 공간 부족
Page 1 | Page 3 (Out of Sequence) | Page 2 |
---|---|---|
R1(크기 증가), R2, R3 | R4(이동됨) | R5, R6, R7, R8 |
✅ 데이터 크기가 커져서 기존 페이지에 맞지 않으면, 새로운 페이지가 생성되고 조각화가 발생함
📌 3. 데이터 삭제(DELETE)에 의한 조각화
📌 삭제 시 조각화가 발생하는 원리
- SQL Server는 데이터를 삭제할 때 즉시 물리적으로 제거하지 않고 "삭제됨(Deleted)" 상태로 표시만 함
- 해당 공간이 비어있지만 새로운 데이터가 자동으로 채워지지는 않음
- 이로 인해 페이지 내에 비어 있는 공간이 많아지면서 내부 조각화(Internal Fragmentation)가 발생함
✅ 삭제 연산은 데이터를 바로 삭제하지 않고 "비어 있는 공간"을 남겨두므로, 인덱스의 효율성이 저하됨
📌 정리
✔ 인덱스 조각화(Index Fragmentation)란?
→ 데이터 삽입/업데이트/삭제로 인해 논리적 순서와 물리적 순서가 일치하지 않는 현상
✔ 조각화 발생 원인
- 데이터 삽입(INSERT): 새로운 데이터가 기존 데이터 사이에 들어가면서 페이지가 분리됨
- 데이터 업데이트(UPDATE): 데이터 크기가 증가하면 기존 페이지에 맞지 않아 새로운 페이지가 생성됨
- 데이터 삭제(DELETE): 삭제된 공간이 재사용되지 않아 내부 조각화가 발생함
13.클러스터형/비클러스터형 인덱스의 개수 제한 및 생성 방식
📌 클러스터형 인덱스(Clustered Index)와 비클러스터형 인덱스(Non-Clustered Index) 개수 제한
✅ 클러스터형 인덱스
- 한 테이블당 최대 1개만 생성 가능
- 데이터 자체를 정렬하여 저장하기 때문에 하나만 존재할 수 있음
- 보통 기본키(Primary Key) 로 자동 생성됨
✅ 비클러스터형 인덱스
- 한 테이블당 최대 999개까지 생성 가능
- 데이터와 별도로 인덱스를 저장하는 방식
- 여러 개의 인덱스를 생성할 수 있지만, 너무 많으면 성능에 악영향을 미칠 수 있음 🚨
📌 비클러스터형 인덱스가 너무 많으면 성능이 저하되는 이유
비클러스터형 인덱스를 많이 생성하면 SELECT 문에서는 성능이 향상될 수 있지만, INSERT, UPDATE, DELETE 연산이 느려질 수 있음.
✅ INSERT(삽입) 시 성능 저하
- 새로운 레코드를 삽입할 때, 클러스터형 인덱스뿐만 아니라 모든 비클러스터형 인덱스에도 값이 추가되어야 함
- 비클러스터형 인덱스가 많을수록 데이터 삽입 속도가 느려짐
✅ DELETE(삭제) 시 성능 저하
- 데이터를 삭제하면, 테이블뿐만 아니라 관련된 모든 비클러스터형 인덱스에서도 데이터를 삭제해야 함
- 비클러스터형 인덱스가 많을수록 삭제 연산도 느려짐
✅ UPDATE(업데이트) 시 성능 저하
- 인덱싱된 컬럼을 업데이트하면, 모든 비클러스터형 인덱스를 수정해야 함
- 업데이트 연산이 많다면 인덱스가 많을수록 성능이 저하될 수 있음
✅ 비클러스터형 인덱스를 생성할 때는 SELECT 성능뿐만 아니라, INSERT/UPDATE/DELETE 성능도 고려해야 함
📌 클러스터형 인덱스 생성 방법
✅ 기본키(Primary Key)를 생성하면 자동으로 클러스터형 인덱스가 생성
📌 테이블 생성 예제 (기본키 자동 생성)
CREATE TABLE Product (
ID INT PRIMARY KEY,
ProductName VARCHAR(255),
ProductDate DATETIME );
위와 같이 PRIMARY KEY
를 선언하면, SQL Server가 자동으로 ID
컬럼에 클러스터형 인덱스를 생성함
✅ 클러스터형 인덱스 생성
CREATE CLUSTERED INDEX CL_Product_ID
ON Product (ID);
✅ 클러스터형 인덱스를 생성하려면 반드시 CLUSTERED
키워드를 명시해야 함
📌 기존 테이블에서 클러스터형 인덱스 확인
SELECT * FROM sys.indexes
WHERE object_id = OBJECT_ID('Product');
✅ 이 명령어를 실행하면 테이블의 인덱스 정보를 확인할 수 있음
📌 클러스터형 인덱스 및 비클러스터형 인덱스 확인 예제
Index ID | Index Name | Index Type |
---|---|---|
1 | PK_Product | Clustered |
2 | IX_ProductDate | Non-Clustered |
✅ 기본키를 설정하면 자동으로 클러스터형 인덱스가 생성되며, 추가적으로 비클러스터형 인덱스를 생성할 수 있음
📌 비클러스터형 인덱스 생성 방법
✅ 비클러스터형 인덱스/복합 인덱스 생성 (기본 방식)
CREATE INDEX IX_Product_ProductDate
ON Product (ProductDate);
CREATE INDEX IX_Product_Name_Date
ON Product (ProductName ASC, ProductDate DESC);
✅ ProductDate
컬럼에 비클러스터형 인덱스가 생성됨
✅ ProductName
컬럼은 오름차순(ASC), ProductDate
컬럼은 내림차순(DESC)으로 정렬된 인덱스가 생성됨
📌 비클러스터형 인덱스의 작명 규칙 (업계 표준)
IX_테이블명_컬럼명
형식으로 인덱스 이름을 지정하는 것이 일반적- 예:
IX_Product_ProductDate
(Product 테이블의 ProductDate 컬럼 인덱스)
📌 클러스터형 인덱스를 기본키가 아닌 다른 컬럼에 적용하는 방법
✅ 예를 들어, ProductDate
컬럼에 클러스터형 인덱스를 적용할 수 있음
📌 기본키는 비클러스터형, 날짜 컬럼에 클러스터형 인덱스 적용 예제
sql
복사
CREATE TABLE Product (
ID INT PRIMARY KEY NONCLUSTERED,
ProductName VARCHAR(255),
ProductDate DATETIME );
CREATE CLUSTERED INDEX CL_ProductDate
ON Product (ProductDate);
✅ 이렇게 하면 기본키는 비클러스터형 인덱스로 유지되며, ProductDate
컬럼이 클러스터형 인덱스로 설정됨
📌 인덱스 확인 결과
Index ID | Index Name | Index Type |
---|---|---|
1 | CL_ProductDate | Clustered |
2 | PK_Product | Non-Clustered |
✅ 기본키가 아닌 컬럼에도 클러스터형 인덱스를 설정할 수 있음
📌 정리
✔ 클러스터형 인덱스는 한 테이블당 1개만 생성 가능 (보통 Primary Key가 자동 생성)
✔ 비클러스터형 인덱스는 한 테이블당 최대 999개까지 생성 가능
✔ 비클러스터형 인덱스를 너무 많이 생성하면 INSERT, UPDATE, DELETE 성능이 저하될 수 있음
✔ SQL Server에서 PRIMARY KEY를 지정하면 자동으로 클러스터형 인덱스가 생성됨
✔ 비클러스터형 인덱스는 필요에 따라 수동으로 생성 가능하며, 여러 컬럼을 포함할 수도 있음
✅ 핵심 : 인덱스는 무조건 많이 만드는 것이 아니라, 데이터 변경 성능도 고려하여 전략적으로 생성해야 함
'백엔드' 카테고리의 다른 글
MySQL 쿼리 최적화: 테이블 스캔, 인덱스 스캔, 옵티마이저, 인덱스 힌트, EXPLAIN ANALYZE (0) | 2025.02.25 |
---|---|
Included Columns(SQL-server) (0) | 2025.02.21 |
인덱스 조각화, 실행계획, 인덱스 scripts (0) | 2025.02.21 |
요구사항 명세서(SRS) (1) | 2025.01.06 |
데이터베이스 관계대수,정규형,트랜잭션,인덱스 트리, 해싱 (0) | 2024.12.12 |