데이터베이스 인덱스 완벽 정복: 성능 최적화의 핵심!
어느덧 데이터베이스 기초 마지막 수업 시간입니다! 그동안 우린 데이터베이스의 다양한 개념을 배웠습니다.
이번 시간에는 데이터베이스의 성능을 좌우하는 인덱스에 대해 살펴보며 데이터베이스 기초 강의를 마무리하도록 할게요!
인덱스의 중요성
데이터베이스의 본질 중 하나는 원하는 데이터를 빠르게 탐색하여 가져오는 것이고, 이를 위해서는 인덱스의 역할이 필수적입니다. 인덱스란 데이터베이스 테이블의 검색 속도를 향상시키기 위한 자료구조이며, 인덱스를 저장하기 위한 추가적인 공간이 필요합니다. 테이블의 모든 데이터를 검색(테이블 풀 스캔)하면 시간이 오래 걸리기 때문에, 데이터와 데이터의 위치를 포함한 자료구조를 생성하여 빠르게 조회할 수 있습니다. 이 자료구조로는 보통 B-트리를 많이 이용합니다.
물론, 테이블에 레코드가 얼마 없으면 인덱스를 사용하는 것보다 풀 스캔이 더 빠를 수도 있습니다. 하지만 대용량 데이터를 다루고 트랜잭션이 많이 발생하는 경우를 항상 가정하고 설계해야 합니다. 따라서 이럴 때 인덱스의 역할은 매우 중요하며, 정확히 알고 쓰지 않으면 오히려 성능이 저하될 수 있습니다.
인덱스의 종류
Clustered Index (클러스터드 인덱스)
클러스터드 인덱스를 지정하는 순간 테이블의 실제 데이터 자체가 정렬된 구조로 계속 유지됩니다. 보통은 Primary Key(기본 키)를 만드는 순간 클러스터드 인덱스가 생긴다고 생각하시면 됩니다.
If you do not define a PRIMARY KEY for a table, InnoDB uses the first UNIQUE index with all key columns defined as NOT NULL as the clustered index.
출처: MySQL Docs
- 클러스터드 인덱스가 지정되면 테이블 내의 모든 데이터는 해당 인덱스 기준으로 정렬됩니다.
- 테이블에 무조건 1개만 존재할 수 있습니다. (우선순위: Primary Key > UNIQUE NOT NULL)
- 정렬되어 있기 때문에 검색 성능이 매우 빠릅니다.
- 하지만 삽입, 수정, 삭제 시 정렬 상태를 유지해야 하므로 성능 저하가 발생할 수 있습니다.
Non-Clustered Index (논-클러스터드 인덱스)
논-클러스터드 인덱스는 데이터 페이지와 인덱스 페이지, 두 개의 페이지로 구성됩니다. 마치 책의 '색인'과 같습니다.
- 데이터 페이지: 실제 데이터를 담고 있으며 정렬되지 않습니다.
- 인덱스 페이지: 인덱스 키와 데이터 행에 대한 포인터(주소)를 담고 있으며 정렬된 상태를 유지합니다.
- 별도의 인덱스 페이지가 필요하므로 추가 저장 공간이 필요합니다.
- 클러스터드 인덱스에 비해 검색은 느리지만, 데이터 페이지는 정렬할 필요가 없으므로 삽입, 수정, 삭제는 더 빠릅니다.
검색 방식
인덱스 유무와 종류에 따라 데이터를 검색하는 방식이 달라집니다.
- Table Full Scan: 테이블의 모든 레코드를 순차적으로 검색합니다. 인덱스가 없거나, 인덱스를 사용하는 것이 비효율적이라고 판단될 때 발생합니다.
- Index Unique Scan: 유니크 인덱스를 사용하여 정확히 하나의 레코드를 찾습니다.
WHERE id = 3;
과 같은 쿼리에서 사용되며, 가장 효율적인 방식입니다. - Index Range Scan: 인덱스의 특정 범위 내에서 검색합니다.
WHERE age BETWEEN 30 AND 40;
과 같은 쿼리에서 사용됩니다. 검색 범위가 넓어지면 성능이 저하될 수 있습니다.
이 외에도 Index Skip Scan, Index Full Scan과 같은 다양한 검색 방식이 있으니 나중에 더 깊이 공부해 보시길 바랍니다!
🚨 인덱스의 단점
인덱스는 만능이 아닙니다. 아래와 같은 단점도 분명히 존재합니다.
- 추가 저장 공간이 필요합니다.
- 데이터 삽입, 수정, 삭제 시 인덱스도 업데이트해야 하므로 오버헤드가 발생합니다.
- 너무 많은 인덱스는 오히려 성능을 저하시킬 수 있습니다.
인덱스 선정 시 꼭 고려해야 하는 것 - Selectivity (분포도)
Q. 인덱스를 사용해도 효과가 거의 없는 경우가 있습니다. 테이블의 레코드가 어떻게 구성되면 이런 현상이 발생할까요?
바로 컬럼의 Selectivity (선택도 또는 분포도)가 좋지 않기 때문입니다. 선택도는 컬럼이 가지는 값의 다양성을 의미합니다.
예를 들어, 헬스클럽 회원 테이블의 '성별' 컬럼에 인덱스를 설정했다고 가정해 봅시다. 성별은 '남' 또는 '여' 두 가지 값만 가집니다. 여기서 여성 회원만 조회(SELECT * FROM members WHERE gender = 'F';
)한다고 해도, 데이터베이스는 전체 데이터의 절반가량을 스캔해야 합니다. 이는 테이블 풀 스캔과 성능 차이가 거의 없습니다.
반면, '이름' 컬럼은 동명이인이 적으므로 선택도가 매우 좋습니다. 특정 이름을 검색하면 훨씬 적은 수의 레코드를 스캔하게 되어 인덱스의 효과를 톡톡히 볼 수 있습니다. 따라서 인덱스를 설정할 때는 선택도가 충분히 좋은 컬럼을 선택하는 것이 매우 중요합니다.
✨ 인덱스 사용 시 주의사항 ✨
- 검색 결과가 전체 데이터의 20%를 넘는다면 인덱스를 사용하지 않는 것이 더 효율적일 수 있습니다.
- 사용하지 않는 인덱스는 저장 공간만 차지하고 CUD 작업에 부하만 주므로 제거하는 것이 좋습니다.
- INSERT만 자주 하는 테이블이라면 굳이 인덱스를 설정할 필요가 없습니다.
WHERE
절에 자주 사용되는 컬럼은 인덱스 설정의 유력한 후보입니다.
인덱스를 잘 사용하고 그렇지 않고에 따라 데이터베이스 성능에 엄청난 영향을 미칩니다. "큰 권력에는 큰 책임이 따른다"는 말처럼, 인덱스를 설계할 때는 항상 신중하게 접근해야 합니다!
'Daily Logs > TIL (Today I Learned)' 카테고리의 다른 글
샤딩 (2) | 2025.06.16 |
---|---|
데이터베이스 인덱스 (1) | 2025.06.14 |
DB part.6 (4) | 2025.06.12 |
DB part.5 (2) | 2025.06.11 |
DB part.4 (1) | 2025.06.10 |