이전 작성 글: [Database] 인덱스
DB Index 동작 원리를 알아보자
[DataBase] DB 성능을 위한 Index
DB 데이터베이스 인덱스(Index) 기본 개념과 설명
Index
라고 하는 것을 사용Index
는 MySQL 에서 사용하는 B+Tree
를 기준으로 함B+Tree
를 사용하지 않는 다른 데이터베이스의 경우 Index
의 동작 원리가 다를 수 있음Index
는 크게 Clustered Index 와 Non Clustered Index 두 가지로 나눌 수 있음Index
가 아닌 MySQL 이 자동으로 설정하는 Index
Auto Increments
값으로 PK
가 있다면 해당 컬럼이 Clustered Index 가 됨PK
가 없다면 컬럼 중에 Unique
컬럼을 Clustered Index 로 선정Unique
컬럼도 하나도 없다면 MySQL 에서 내부적으로 Hidden Clustered Index Key
( row ID ) 를 만들어 Clustered Index 로 사용Clustered Index Key
로 사용하는 위 3가지 조건을 살펴보면 공통점 존재count(*)
과 distinct key
한 값이 똑같다는 점Index
는 높은 효율을 보임Index
는 Non Clustered Index 에 해당Index
의 경우 최대 16개의 컬럼을 사용할 수 있고 테이블 당 인덱스의 개수는 최대 64개까지 지정이 가능Index
의 저장 구조로 B+Tree
를 사용이진 트리
< B-Tree
< B+Tree
순으로 확장되어진 자료구조Node
혹은 Page
라고 부름Node
의 사이즈가 16KB 로 설정되어 있음Node
중에서 최상단 레벨에 존재하는 Node
를 Root Node
라고 하고 최하단에 디스크의 주소를 가지고 있는 Node
레벨을 Leaf Node
라고 함Root
와 Leaf
사이에 있는 Node
들을 Branch Node
라고 부름# 예시
select name from table where age=22;
age
컬럼이 Index
로 설정되어 있다고 가정하고 이 쿼리에서 Index
를 통해 데이터를 조회하는 방법Root Node
로 가서 age=22
인 값의 Leaf Noe
로 가기 위한 경로를 안내 받음Root Node
는 Branch Node
의 경로를 안내해줌Branch Node
가 없어서 Root Node
바로 아래 Leaf Node
가 있다면 바로 Leaf Node
의 위치를 알려줄 것Branch Node
로 가면 또 아래의 Branch Node
혹은 Leaf Node
의 경로를 알려줄 것Leaf Node
까지 도착하게 되면 Leaf Node
는 Index
의 값과 디스크의 주소값을 가지고 있음select name
이 아닌 select age
로 조회하는 경우B+Tree
를 통해 age
값이 22 인 것을 이미 알고 있으니 디스크로 접근할 필요 없음age
가 아닌 name
을 조회했기 때문에 디스크까지 접근해야 함age
22
에 저장되어 있는 디스크 B 주소로 접근하게 됨철수
라는 데이터를 가져오고 조회 결과를 보여주게 됨Index
로 설정해두면 데이터양이 많아질수록 Full Scan 보다 Index Scan 이 더 빠름Index
를 통한 검색은 B+Tree
에서 Leaf Node
까지 찾아 내려간 후 해당 데이터를 찾기 위해 디스크로 접근B+Tree
를 찾아가는 과정 없이 디스크로 가서 바로 모든 데이터를 읽어옴Index
가 효율적으로 설정되어 있지 않은 경우 오히려 Table Full Scan 이 더 빠름Node
의 사이즈는 16KB 이지만 예제로 살펴보기 위해 한 개의 Node
는 최대 3개의 데이터를 저장할 수 있다고 가정B+Tree
의 Node
에서 데이터가 Overflow 발생했을 때 어떤 일이 일어날까?Node
에 새로운 데이터인 23
이 들어오려고 함Index
는 모든 데이터가 정렬된 상태로 저장하기 때문에 23
은 20
과 25
사이에 들어가려고 할 것Node
로 올라가게 되고 해당 Node
는 Leaf Node
를 가리키는 Node Number
를 저장하게 됨B+Tree
기준으로는 예제에서 데이터 사이즈가 3개인 경우 새로운 데이터가 들어오면서 4개가 된 경우 n/2+1
번째의 데이터가 상위로 올라감4/2+1=3
이므로, step. 3 처럼 23
이 상위로 올라감Node
에 데이터가 넘치는 경우 Node
가 2개로 나눠지는 Split 과정이 일어남Node
에 1개씩만 저장된 Node
가 많다고 가정한다면, 이런 경우 공간 낭비를 줄이기 위해 MySQL 이 2개의 Node
를 1개로 병합하는 Merge 동작이 일어남B+Tree
에 총 10개의 데이터를 넣는 경우25
를 넣었을 때Node
에는 총 3개의 데이터를 저장할 수 있고 자리는 매우 널널함25
가 그대로 저장되고 종료16
을 넣었을 때Index
는 항상 정렬된 순서로 저장함25
보다 왼쪽에 저장됨20
을 넣었을 때16
과 25
사이에 저장됨23
을 넣었을 때23
을 넣으려고 했더니 Node
의 사이즈가 꽉 차는 상황이 발생n/2+1
에 의한 3번째 데이터인 23
은 상위 Node
로 올라가야 함23
이 상위 Node
로 올라가면서 Root Node
와 Leaf Node
2단계로 나뉘어짐Root Node
가 있으니 데이터가 바로 저장되지 않음Root Node
부터19
를 저장하기 위한 Leaf Node
를 찾아야 함Root Node
에 있는 23
에 가서 19
가 가야할 Leaf Node
의 위치를 확인19
는 23
보다 작기 때문에 왼쪽의 Leaf Node
의 위치를 알려줄 것Root Node
의 왼쪽 경로를 따라 내려가서 만난 Leaf Node
에 19
를 저장하게 됨17
를 넣기 위해 동일하게 Root Node
부터 탐색Leaf Node
로 따라감17
을 저장하고 보니 Node
가 넘치는 문제가 발생하면서 Split 과정이 일어남19
가 상위 Node
로 올라가게 됨22
를 저장하기 위해 Root Node
를 살펴보면 22
는 19
와 23
의 사이값Root Node
에서 알려준 가운데 Leaf Node
로 와서 22
데이터를 저장21
을 저장하기 위해 Root Node
에서 경로를 찾아감Leaf Node
를 알려줄 것21
을 저장하고 보니 Node
사이즈가 넘치게 되어 Split이 일어남21
이 상위 Node
로 올라가게 됨18
을 넣기 위해 Root Node
를 살펴보면 가장 왼쪽 경로를 통해 Leaf Node
를 찾아감18
을 저장하고 종료15
를 넣으려고 하면 역시 19
보다 작으니 가장 왼쪽 경로를 따라 찾아가게 됨15
를 저장하고보니 Leaf Node
의 Split이 필요함Leaf Node
를 두 개로 나눴고 17
을 상위 Node
로 복사Root Node
에서 Node
가 넘치는 상황 발생Root
에서도 동일한 조건으로 Split 과정이 일어남Leaf Node
와의 Split 차이점은 상위로 데이터를 저장할 때 중복으로 저장하지 않음Leaf Node
는 모든 데이터와 디스크 주소값을 가지고 있는 반면, B+Tree
에서 Root Node
와 Branch Node
는 하위 Node
의 경로값만 가지고 있음Root
, Branch
, Leaf
3단계가 모두 있는 B+Tree
구조가 되었음Insert
할 때와 동일하게 Root Node
부터 Leaf Node
를 찾아가는 과정은 동일19
라는 데이터를 삭제할 때Root Node
→ Branch Node
→ Leaf Node
순서로 데이터의 위치를 찾아갈 것19
라는 데이터를 찾았을 때 Index
에서 해당 데이터를 삭제19
를 삭제하고 보니 Branch Node
에서 19
를 가리키고 있었는데, 19
라는 데이터가 삭제되어 문제 발생Branch Node
는 해당 Leaf Node
에서 가장 작은 값인 20
으로 다시 저장하는 동작이 일어남Branch Node
의 값을 20
으로 다시 바꿔주고 삭제 작업이 끝나게 됨B+Tree
에서 Update
라는 명령은 없음Update
를 실행하게 되면 Delete
→ Insert
순으로 동작이 일어남Delete
를 하면서 Node
에 빈 공간이 많아질 경우 두 Node
가 합쳐지는 Merge
과정이 발생할 수 있고, 위에서 살펴본 것처럼 Leaf Node
를 가지고 있는 값이 삭제되는 경우 Branch Node
가 가리키는 값을 바꿔야하는 경우가 발생할 수 있음B+Tree
의 동작 과정을 생각해보면 답변이 아니요
라는 것을 알 수 있음Select
는 Index
에 의해 더 빨라지겠지만 Update
, Delete
, Insert
의 동작이 더 느려지게 됨Index
가 1개면 위 과정이 1번이겠지만 Index
가 20개라면 20번 실행되어야 함Tip! Cardinality 란?
- 카디널리티는 전체 행에 대한 특정 컬럼의 중복 수치를 나타내는 지표
- 중복도가 '낮으면' 카디널리티가 '높다'고 표현
- 중복도가 '높으면' 카디널리티가 '낮다'고 표현
Index
의 손익분기점이라고 표현하는데, 상황에 따라 다르겠지만 보통 전체 데이터의 5 - 10% 정도로 걸러지는 경우 index
를 사용했을 때 좋은 효율을 낼 수 있음Index
로 사용하는게 좋음explain
을 통해 쿼리가 어떻게 실행되는지 살펴볼 수 있음where
에 Index
컬럼을 사용했지만 Full Scan 으로 동작B+Tree
는 해당 Index
의 데이터값을 그대로 저장하고 있음Index
를 통한 검색이 불가능# 예시 1
where price * 0.9 > 10000
# 예시 2
where price > 10000 / 0.9
price
가 Index
로 설정되어 있다고 하더라도 price * 0.9
에 대한 결과값은 알 수 없음0.9
를 오른쪽으로 보내어 쿼리를 실행해야 Index
를 통한 검색을 할 수 있음where
을 사용하는 경우 해당 데이터를 제외한 모든 데이터를 검색해야 함B+Tree
는 데이터의 첫 글자를 기준으로 정렬하여 저장하고 있음like
쿼리를 통해 앞에 %
를 붙이는 경우 모든 데이터를 찾아야하는 Full Scan 으로 동작하게 됨count(*)
라는건 모든 데이터를 한바퀴 돌아야 몇 개인지 알 수 있음age
, name
으로 멀티 컬럼 Index
가 설정된 경우 B+Tree
는 age
로 정렬한 후 name
으로 정렬한 데이터를 저장하고 있음name
만을 조건으로 사용하면 Index
를 통한 검색을 할 수 없음age
가 첫번째 조건이기 때문에 where age
만 하면 Index
검색이 됨age
, name
순으로 Index
를 지정하고 나서 where name, age
로 조건을 주는 경우, B+Tree
는 age, name
순으로 정렬된 데이터를 가지고 있기 때문에 Index
검색을 할 수 있음where
을 주더라도 데이터베이스에 존재하는 옵티마이저가 컬럼 순서를 바꿔서 조회Index
검색이 되기는 함Index
로 실행되지 않는 경우