컴퓨터 공부/🎮 테크레터

🎮 [테크레터 2편] 인덱스 Index ?

letzgorats 2024. 1. 4. 19:18

인덱스라는 말 들어보셨나요?

덱스에 빠져버리고 싶은 마음... 이것이 인덱스? 아닙니다.

2023 신인남자 예능인상 덱스님 축하드립니다

 

오늘 설명드릴 인덱스(Index)는 데이테베이스에서 자주 접할 수 있는 개념입니다!

 

Index 란 말 어디서 들어보셨죠?

인덱스는 책 맨 뒷 편에서 볼 수 있거나 찾아보기 란에서 종종 볼 수 있습니다.

https://www.jstwrite.com/blog/10-steps-to-creating-your-books-index

 

책에서 이런 페이지를 제공하는 이유는 책을 다 읽지 않고도 원하는 정보만 빠르게 찾아서 해당 위치만 읽을 수 있도록 하기 위함입니다.

데이터베이스에서의 인덱스도 마찬가지입니다. 데이터베이스의 인덱스는 검색 속도를 향상시키기 위한 일종의 자료구조입니다.

 

강아지 정보가 있는 테이블에서 species(종)이 '웰시코기'인 값을 찾는다고 해봅시다.

full table scan

 

일반적인 경우에 전체 데이터를 조회하면서 species(종)이 '웰시코기'인 것을 매번 비교하고 해당 결과들을 모아서 반환해 결과로 보여줍니다. 이런 full table scan 동작은 만약 데이터 테이블이 굉장히 늘어났을 때, 해당 연산을 똑같이 선형적으로 많이 늘어서 해야 한다단점을 쉽게 발견할 수 있습니다.

이런 방식은 굉장한 성능 저하를 부르기 때문에, 반드시 개선해야 하는 작업이죠.

 

인덱스는 그럼 어떻게 검색 속도를 빠르게 향상시킬 까요?

인덱스에 대해 이해하려면 먼저 B-tree 라는 자료구조에 대한 이해가 선수되어야 합니다.

왜냐하면, 인덱스가 일반적으로 B-tree의 자료구조를 통해 검색 속도 향상을 수행하기 때문입니다.

그럼 먼저, B-Tree 에 대해 살펴봅시다. 


✅ B-tree

B-tree 는 Balanced tree 의 약자로 균형잡힌 트리를 의미합니다.

 

이 B-tree 의 특징은 크게 아래와 같습니다.

  1. 균형 트리
  2. 탐색 트리 (정렬)
  3. 하나의 노드에 여러 정보 저장 가능
  4. 두개 이상의 자식을 가질 수 있다.

.

..

...

아니 이게 무슨 의미인가요? 하실 수 있습니다.

그럼 각각의 특징들에 대해서 잠깐 살펴보고 가겠습니다.

 

1. 균형 트리

B-tree 가 아닙니다.

 

위 그림에서 노드(알루)가 균형잡힌 트리를 만들고 있다고 생각하시나요? 뭔가 비대칭적으로 보입니다.

그럼 아래 그림을 살펴보실까요?

 

균형 트리

 

이번에는 알루가 아주 예쁘게 트리를 구성하고 있는게 보입니다.

그림과 같이 트리들은 전반적으로 한쪽으로 치우쳐져 있지 않고 적당히 분배하여 노드들을 가지고 있습니다.

(노드가 '알루'라고 생각하세요)

 

→ 이렇게, B-tree 는 여러 자식 노드를 가질 수 있는 "균형 잡힌 트리"라고 이해하면 됩니다.

 

이런 균형 잡힌 트리는 검색을 할 때, 항상 최적의 성능을 보장하기 위한 특성 중 하나입니다.


2. 탐색 트리

탐색 트리

 

탐색 트리는 어떤 값을 기준으로 매번 정렬이 되어 있는 상태를 유지하는 특징이 있습니다.

 

그럼 이런 탐색 트리의 원리가 무엇일까요? 예를 들어, 위 트리에서 11 이라는 값을 찾는다고 가정해봅시다!

그럼, 아래와 같은 탐색 로직을 따를거에요.

 

예를 들어, 11 이라는 값을 찾는다 하면,

→ 11은 8보다 크니까 오른쪽으로 탐색

→ 11은 12보다 작으니까 왼쪽으로 탐색

→ 11은 10보다 크니까 오른쪽으로 탐색

→ 11은 11 과 같으니까 탐색 종료(find 11 완료)

 

→ 이렇게, B-tree 는 내가 원하는 값을 전체 다 조회하지 않고도 반씩 소거해가면서 쉽고 빠르게 찾아볼 수 있는 특징이 있습니다.


하지만, 항상 정렬을 유지하는 특성 때문에, 반대로 데이터 삽입이나 삭제 또는 업데이트(수정) 같은 부분에 되게 취약한 성능을 보입니다.

 

 

예를 들어서, 6이라는 값을 넣는다고 가정해보죠.

삽입

 

그럼 우선, 6이 들어가야 할 위치를 찾아야 하고

이 6이라는 값을 저장을 했을 때, 전체적인 B-tree 가 균형을 맞췄는지 확인을 해보고

해당 균형을 맞추기 위해서 추가적인 여러 연산이 필요합니다.

삽입 과정

 

검색 성능 good, 쓰기 성능 bad

 

→ 이렇게, B-tree(탐색트리) 항상 정렬 상태를 유지하기 때문에, 쓰기 성능을 굉장히 많이 희생하면서 검색 성능을 최대한 끌어올린다는 특징이 있는 것이죠.


3. 하나의 노드에 여러 정보 저장 가능 / 4. 두 개 이상의 자식을 가질 수 있다.

하나의 노드에 두 개의 데이터 & 자식도 많이 가질 수 있다

 

→ 이렇게, B-tree(탐색트리)하나의 노드에 여러 정보를 저장할 수 있고, 이와 동시에 두 개 이상의 자식들을 가질 수 있다는 특징이 있습니다. 이렇게 하면, 기존에 절반씩 소거했던 것을 3분의 1, 4분의 1, 3분의 2 씩 화끈하게 소거해가면서 값을 찾을 수 있습니다. 또한, 똑같은 트리 구조에 훨씬 더 많은 데이터가 들어갈 수 있다는 장점도 있습니다.

 


번외) B+ tree

(※ 실제 데이터베이스들은 B tree 말고 B+ tree 라는 형태로 데이터를 보관합니다.)

 

B+ tree는 데이터를 노드마다 보관하는 것이 아니라, 가장 아래 노드에만 데이터를 보관하는 구조입니다.

위의 노드들에는 데이터를 찾기 위한 가이드만을 제공하는 셈이죠.

그리고, 맨 아래 노드(알루)끼리도 화살표로 연결을 해둡니다. 이렇게 하면 범위검색(range 검색)이 매우 용이해지는 장점이 있습니다.

 

예를 들어, 4부터 15까지 숫자를 찾으려고 해볼게요.

B+ tree 에서의 범위 검색 과정

 

B tree 에서는 계속 위 아래로 움직여야 하는 번거로움이 발생하지만, B+ tree에서는 

4부터 찾은 다음에 계속 데이터 노드끼리의 화살표를 타고 다음 노드로 이동만 하면 끝나는 것이죠.

 


그럼, 이런 B-tree 구조를 가지는 인덱스로 조회를 할 때는 어떻게 검색이 되는지 살펴봅시다!

 

✅  Index 로 조회

index 조회 과정

 

우선, 데이터들을 위 그림과 같이 트리 구조로 나눠 갖게 됩니다. 이 때, "페이지" 라는 단어가 등장하는데요, 페이지는 데이터를 읽고 쓰는 최소 작업 단위로서 지금은 그냥 일종의 노드로서 이해하면 됩니다.

따라서, species 기준으로 정렬되는 b-tree 구조를 따라서, '품종'이 웰시코기인 값을 찾는다면 위와 같은 과정으로 탐색을 하게 되는 것입니다. Full Scan 방식 보다 훨씬 빠르게 원하는 정보를 찾을 수 있게 된 셈이죠.


 

이런 Index 는 크게 두 가지 종류로 이해해 볼 수 있는데요, Clustered Index 와 Non-clustered Index 입니다.

 

1. Clustered Index

Clustered Index 에서 Cluster는 무리 또는 군집이라는 의미를 가집니다. 그래서, 클러스터드 인덱스는 실제 데이터가 군집을 이룬다고 하는데요, 실제 데이터와 군집(강한 연관)을 이루는 인덱스데이터가 테이블에 물리적으로 저장되는 순서를 정의합니다.

(실제 데이터에 저장되는 물리적인 주소가 변경이 되는 것이죠!)

 

→ Index 생성 SQLClustered Index는 데이터베이스 구성 시에 PK(기본키)로 설정을 하면, 자동으로 생성이 되기 때문에 PK 인덱스라고도 부를 때가 있습니다. serial number로 값을 주는 id 같은 칼럼이 이에 해당하겠죠.

또, unique 와 not null 제약 조건을 걸어도 자동으로 생성이 됩니다.

 

 

그럼, 예시로 들었던 강아지 테이블아이디(id)를 기준으로 클러스터드 인덱스를 생성해보겠습니다.

클러스터드 인덱스

 

인덱스는 실제 데이터와 연관이 있기 때문에, id 값으로 정렬을 하면서 실제 데이터들이 저장되어 있는 주소가 변경되게 됩니다.

그리고 해당 데이터에 인덱스가 생성되었기 때문에 아이디가 6인 값을 찾는다 하면, 인덱스를 타고 가서 실제 데이터가 저장되어 있는 곳에 접근하여 데이터를 조회할 수 있다는 특징이 있습니다.


 

💡 Clustered Index 에 대해 정리를 해보면, 아래와 같습니다.

1. 데이터를 정렬함
2. Index 의 리프페이지가 실제 데이터들이 저장되는 공간
3. 따라서, 테이블당 1개만 존재

 


이제 Non-clustered Index 에 대해서도 살펴봅시다.

 

2. Non-Clustered Index

Non-Clustered Index 는 Index를 따로 SQL문으로 생성할 수 있습니다.

 

Index 생성 SQL

CREATE INDEX name_idx ON dog(name)

 

index의 이름어떤 테이블어떤 컬럼을 설정할지 명명할 수 있는데, 이 때 컬럼은 여러개를 둘 수 있습니다. 특히, 컬럼을 명명하는 순서에 따라 성능에 굉장히 많은 영향을 미치므로 이점을 고려해서 생성해야 합니다. 

 

→ 또, unique 제약 조건을 걸어도 자동으로 생성이 됩니다.

 

그럼, 예시로 들었던 강아지 테이블 이름(name)을 기준으로 논-클러스터드 인덱스를 생성해보겠습니다.

 

 

앞서, 클러스터드 인덱스의 예시와는 다르게 실제 데이터들은 가만히 있습니다.(오른쪽)

그리고 강아지 name(이름)을 기준으로 정렬되어 있는 인덱스 페이지만 따로 생성하게 되는 것이죠.(왼쪽)

여기서 가장 큰 특징은 실제 데이터와 연관되어 있지 않기 때문에, 인덱스의 리프페이지에는 데이터의 실제 값들이 아니라 데이터가 저장되어 있는 그 주소값을 가지고 있습니다.

 

non-clustered index 탐색 과정

 

그래서 만약에 이름이 '알루'라는 강아지를 찾을 때, 이름으로 정렬되어 있는 인덱스를 타고 가서 주소 값을 알아낸 뒤, 그 후에 해당 데이터에 접근하게 되는 것입니다.

 


 

그럼 이번에는 강아지 테이블  species(종) 기준으로 논-클러스터드 인덱스를 생성해보겠습니다.

인덱스를 생성하는 SQL 구문은 아래와 같습니다.

CREATE INDEX species_idx
ON dog(species)

 

(※ 참고 - postgreSQL 에서 생성한 index를 조회하는 방법은 아래와 같습니다.)

SELECT * FROM PG_INDEXES WHERE TABLENAME = 'dog';

 

(※ 참고 - mySQL 에서 생성한 index를 조회하는 방법은 아래와 같습니다.)

SELECT * FROM USER_INDEXES WHERE TABLE_NAME = '테이블명';

 

non-clustered index 탐색 과정

 

 

이 역시도 실제 데이터들은 정렬될 필요가 없고(오른쪽), 그냥 species(종)으로 정렬되어 있는 인덱스 페이지만 생성(왼쪽)하고 해당 데이터들을 가리키게 하면 됩니다.

 

즉, non-clustered 인덱스에서는 이런식으로 한 테이블에 여러 인덱스 페이지를 생성할 수 있습니다.


 

💡 Non-Clustered Index 에 대해 정리를 해보면, 아래와 같습니다.

1. 인덱스와 데이터 페이지가 따로 존재
2. 리프페이지에선 데이터 주소값을가짐
3. 따라서, 데이터 페이지가 따로 정렬되지 않아도 됨
4. 한 테이블에 여러개가 존재할 수 있음

 

※ 그러면, clustered index 와 non-clustered index 가 동시에 존재할 때는 어떻게 동작할까요?

 

아까 species 에 대해 non-clustered 인덱스를 생성하고 생성한 index를 조회하는 SQL 문의 결과는 아래와 같이 나타납니다.

 

만든 index 조회

 

아주 초기에 dog 테이블을 생성할 때, id 값을 Primary Key 로 줬기 때문에, id는 clustered index 인 pk 인덱스로 인식된 것을 볼 수 있습니다. 즉, 지금 pk인덱스인 clustered index 와 species 인덱스를 따로 생성한 non-clustered index 가 공존하고 있다는 말이죠.

 

이렇게, 테이블을 생성할 때 보통의 경우 PK 컬럼을 설정을 해주는데, 이는 Clustered Index 이고, 

자주 발생하는 조회 컬럼에 대해 Index를 생성해주면, 이는 Non-Clustered Index 가 됩니다.

 

clustered & non-clustered 공존할 때

 

클러스터드 인덱스는 크게 달라지는 점은 없습니다.

하지만, 논-클러스터드 인덱스에서 이전에는 데이터 주소를 직접 가르키고 있었는데, 이제는 클러스터드 인덱스에서 걸어둔 컬럼 혹은 pk 값을 가지게 됩니다.

 

이런 구조에서, 다시 강아지 종(species)이 '닥스훈트'인 강아지를 검색해 보면, 동작 구조는 아래 그림과 같습니다.

clustered & non-clustered 공존할 때 동작구조

 

먼저, Non-clustered Index 페이지에서 species 값을 통해 인덱스를 타고 PK 값을 찾아냅니다

그 후, 해당 PK를 다시 클러스터드 인덱스인 PK 인덱스를 타고 데이터에 접근하는 순서로 동작을 합니다.

 

 

어떻게 보면, 검색에 있어서 성능이 조금 손실을 봤다고 이해할 수 있습니다. 두 번의 단계를 거쳐야 하니까요.

하지만, 이렇게 동작하는 이유는 단순히 검색을 위해서가 아니라 PK 쓰기작업에 대한 유연성을 갖추기 위해서 입니다.


 

예를 들어서, 다양한 인덱스들이 있고 클러스터드 인덱스가 모두 데이터 주소를 가리키고 있다고 해볼게요.

직접적으로 가리킬 때

 

근데, 이 상태에서 PK 값이 변경되거나 추가되는 작업이 일어났을 때, 해당 주소를 다 가리키고 있던 인덱스에서 이 변경을 다 감지할 수 있어야 하고 수정 작업이 다 이루어져야 합니다.

직접적으로 가리킬 때 데이터 변경되면 막대한 연산 비용 발생

 

이런 막대한 연산 비용을 줄이기 위해서, PK 값을 저장하고 PK 에 대한 변경 사항에 대해서는 클러스터드 인덱스를 통해서를 하는 식으로 동작하게 되는 것입니다.

 

clustered & non-clustered 공존할 때 동작구조

 


 

※ 그러면, 지금까진 배운 Index 는 어디에 걸면 좋을까요?

1. WHERE, JOIN, ORDER BY 와 같은 조건에 자주 발생하는 컬럼들
2. INSERT, UPDATE, DELETE 가 적게 발생하는 컬럼들 (쓰기 작업이 적게 발생하는 컬럼)
3. 중복도가 낮은 컬럼
4. 범위 검색이 적은 컬럼
5. 데이터가 많은 테이블 ( 데이터가 많으면 많을수록 Full table Scan과의 성능차이가 두드러집니다 )

 

→ 인덱스는 컬럼을 복사해서 정렬을 해두는 개념이기 때문에, 만들때마다 DB 용량을 그만큼 더 차지할 수 있습니다.

그래서, 검색작업이 필요없는 컬럼들은 굳이 인덱스를 만들어 둘 필요는 없어요

 

→ 쓰기작업(삽입,삭제,수정) 이 자주있는 컬럼들에 대해서는 index에도 변경을 반영해야하기 때문에 성능하락의 가능성이 존재합니다. 때문에, 이 부분도 고려해야 합니다! (*크게 신경쓸 필요는 없지만)

 

이처럼, 인덱스 index 를 필요에 따라 설정해준다면, 많은 성능 향상을 기대할 수 있습니다.

이 외에도 index와 관련한 개념으로, 아래와 같은 많은 개념이 있으므로, 이런 키워드로 개인적으로 공부하면 오늘 테크레터 내용이 더 이해가 잘 가실 수 있습니다!

1. Covering index
2. Cardinality
3. 다양한 Index Scan 방식
4. Page/block 및 버퍼
5. 메모리, 디스크 I/O 접근방식
6. B+tree

[참고자료]

- 쉬운코드 유튜브 - B tree가 왜 DB 인덱스(index)로 사용되는지를 설명합니다.

- [10분 테크톡] 찰리의 인덱싱

- 코딩애플 유튜브 - index가 뭔지 설명해보세요(개발면접시간)

- MySql Optimization and Indexes

반응형