참고 자료
[MYSQL] 📚 인덱스(index) 핵심 설계 & 사용 문법 💯 총정리
[MYSQL] 📚 인덱스(index) 핵심 설계 & 사용 문법 💯 총정리
인덱스의 개념 인덱스란 데이터의 저장(INSERT, UPDATE, DELETE) 의 성능을 희생하고 그 대신에 데이터의 읽기 속도를 높이는 테이블의 동작속도(조회)를 높여주는 자료구조이다. 쉽게 예를 들어보면
inpa.tistory.com
https://www.youtube.com/watch?v=IMDH4iAQ6zM
Index 유무에 따른 성능 차이
- where first_name = ‘Minsoo’; 쿼리를 날렸을 때 성능을 비교해보자.
- first_name에 Index가 걸려 있지 않다면, 테이블의 로우를 순차 탐색하므로 O(N)
- first_name에 B tree 기반 Index가 걸려 있다면, O(logN)
- 튜플 검색의 기초가 되는 PK는 기본적으로 Index가 걸려있다.
Index를 사용하는 이유
- 조건을 만족하는 튜플(들)을 빠르게 조회하기 위해
- 빠르게 정렬(order by)하거나 그룹핑(group by)하기 위해
Index 사용 예시1
- select * from player where name = ‘Sonny’;
- 위의 쿼리를 수행할 때, name에 Index를 걸면 더 빠르게 동작한다.
- CREATE INDEX player_name_idx ON player(name);
Index 사용 예시2
- select * from player where team_id = 105 and backnumber = 7;
- player 테이블에서 team_id와 backnumber는 둘의 조합으로 player별 고유한 값을 가진다. 둘을 같은 Index에 걸면 더 빠르게 동작한다.
- CREATE UNIQUE INDEX team_id_backnumber_idx ON player(team_id, backnumber);
B-tree 기반의 index 동작 방식
- a 컬럼에 건 Index의 구성 : 정렬된 a 컬럼과 a에 연결된 튜플의 포인터
- where a = 9;을 실행할 때 Index(a)에서 이진 탐색으로 9를 빠르게 찾고, 9의 ptr가 가리키는 튜플을 찾아서 조회함
- Index(a)만 있을 때, a와 b를 함께 조건 검색하는 경우 성능이 안 나온다. full scan과 별 차이가 없다.
- Index에서 조건에 맞는 a를 찾으면 포인터로 튜플의 b를 추가 탐색한다. 만약 조건에 맞는 a로 찾은 포인터가 수천건이 넘는다면, 이 데이터에 대해 매번 조회해서 b를 비교해야한다.
- 위의 상황에서 성능을 내려면 Multiple Index를 걸어야 한다. 먼저 선언한 컬럼 위주로 정렬된다.
- 이진 탐색으로 a 조건에 맞는 Index 튜플을 찾으면 b 조건을 추가로 비교해서 맞으면 포인터로 연결된 튜플을 선택한다.
- 튜플의 앞뒤로 비교하여 a 조건을 비교하고, a 조건이 일치하는 범위에서 b 조건을 비교한다. 이때 b 조건도 이진 탐색한다.
- Multiple Index를 건 상태에서 주요 키가 아닌 키로 검색을 시도하면 성능이 안 나온다.
- Index에서 선언한 키 순서대로 정렬되므로, b는 정렬된 상태가 아니다. 그냥 풀 스캔한다. 이럴 때는 b 전용 인덱스를 걸어야 한다.
- where a = 7 or b = 95도 마찬가지로 성능이 안나온다.
INDEX 선택
- 어떤 select 절을 실행할 때 어떤 Index를 탈지 궁금하다면 select 절 앞에 EXPLAIN 키워드를 사용한다.
- select 절을 실행할 때 Index를 선택하는 것은 보통 Optimizer이다. 간혹 Index를 걸었는데도 성능이 안 나오는 문제가 있다면 Optimizer가 잘못된 인덱스를 선택했을 수도 있다.
- 수동으로 인덱스를 지정할 수 있는데, select 절 실행 시 where 절 앞에 USE INDEX(인덱스)나 FORCE INDEX(인덱스)를 사용한다.
INDEX를 막 만들어도 될까?
- INDEX를 만들면 실제 테이블에 있는 컬럼 데이터가 복사되어 중복으로 존재한다. 저장 공간이 더 필요하다.
- 만약 원본 테이블에 write하면 Index에도 변경이 발생한다. 이진 탐색 트리로 정렬하는 연산도 필요하다. 오버헤드 발생.
- 불필요한 인덱스는 만들지 않는 것이 중요하다!
Covering Index
- Index(team_id, backnumber)가 존재하는데, team_id와 backnumber를 조회하는 select 실행
- 조회하는 속성을 index가 모두 커버하고 있으므로 포인터로 조회할 필요가 없다. 조회 성능이 더 빠르다.
Hash Index
- 해시 함수 기반의 인덱스 구현 방식이다.
- 조회의 시간 복잡도가 O(1)이다.
- 단점 : rehash에 대한 부담, 동등 비교만 가능하고 범위 비교가 불가능하다. multicolumn index의 경우 컬럼 일부만 사용할 수 없다.
Full Scan이 더 좋은 경우
- 테이블에 데이터가 조금 있는 경우 (몇 십, 몇 백 건) → 데이터가 많이 쌓여서 조회 성능에 이슈가 나면 인덱스 지정한다.
- 조회하려는 데이터가 테이블의 상당 부분을 차지할 때. 즉, 타입 정보 같이 카디널리티가 낮은 컬럼으로 검색할 때.
Index 설계 TIP
- WHERE, JOIN 절에 자주 사용되는 열에는 인덱스의 효율이 좋다.
- ORDER BY 절에 사용되는 열은 데이터 페이지가 자동 정렬되는 클러스터형 인덱스가 유리하다.
- 데이터 중복도가 높은 컬럼은 인덱스 효과가 없다.
- 데이터 Insert가 조회보다 잦은 테이블은 성능 저하가 발생할 수 있다.
- 대용량 데이터가 자주 입력되는 경우 클러스터 대신 보조 인덱스를 사용하는 것이 낫다.