
이전 작성 글: [Database] 인덱스
DB Index 동작 원리를 알아보자
[DataBase] DB 성능을 위한 Index
DB 데이터베이스 인덱스(Index) 기본 개념과 설명
Index 라고 하는 것을 사용Index 는 MySQL 에서 사용하는 B+Tree 를 기준으로 함B+Tree 를 사용하지 않는 다른 데이터베이스의 경우 Index 의 동작 원리가 다를 수 있음Index 는 크게 Clustered Index 와 Non Clustered Index 두 가지로 나눌 수 있음Index 가 아닌 MySQL 이 자동으로 설정하는 IndexAuto 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 로 실행되지 않는 경우