[CS 학습] 인덱스란?
1.1 디스크 읽기 방식
1.1.1 HDD와 SSD 차이
기존 자기방식의 HDD에 비해 flash memory (기록 보존이 가능한 반도체)를 활요한 SSD의 성능이 더 뛰어남
SSD의 경우 헤더를 움직이지 않기 때문에 작은 데이터를 읽고 쓰는 랜덤 I/O에서 큰 성능차이를 보임
DBMS에서는 작은 데이터를 읽고 쓰는 작업이 대부분 이므로 SSD가 최적
1.1.2 랜덤 I/O와 순차 I/O
랜덤 io | 데이터 마다 읽어야할 데이터로 디스크 헤더를 이동 |
---|---|
순차 io | 한번만 읽어야할 데이터로 디스크 헤더를 이동 |
→ 데이터를 여러번 쓰고 읽을 때에는 랜던 io에 더 많은 시간이 소요됨 (헤더의 이동시간이 병목이 되므로)
헤더가 없는 SSD의 경우 순차 io보다 랜덤 io의 전체 스루풋이 떨어짐 (좀더 확인해보기)
- SSD의 경우 내부적으로 논리적인 주소를 물리적인 주소로 맵핑하는 맵핑테이블을 사용
- 매핑 테이블을 업데이트하려면 플래시 메모리의 물리적인 위치를 찾아가야 하므로 랜덤 I/O의 경우에는 이 과정이 추가로 발생하여 전체 처리율이 떨어짐
- 순차I/O의 경우 논리적인 위치를 통해 알아낸 물리적인 위치를 통해 나머지 항목들도 업데이트
1.2 인덱스란?
인덱스란 일종의 책의 색인 (찾아보기)
<칼럼값, 저장된 주소>의 키와 값의 쌍을 갖는다
DBMS의 인덱스 자료구조는 SortedList를 가진다.
SortedList | ArrayList | |
---|---|---|
자료 순서 기준 | 값이 저장될때 마다 정렬됨 | 값이 저장된 순서 |
쓰기, 수정, 삭제 | 느림 | 빠름 |
읽기 | 빠름 | 느림 |
sortedList의 경우 쓰기(INSERT), 수정(UPDATE), 삭제(DELETE)의 경우 속도가 느리지만 읽기(SELECT)의 경우 속도가 빠르다.
인덱스의 역할별 분류
- 프라이머리 키 (PK)
- 그 레코드를 대표하는 칼럼의 값으로 만들어진 인덱스
- 식별자라고도 불리며 NULL을 허용하지 않는다
- 세컨더리 인덱스
- PK를 제외한 나머지 인덱스
- 유니크 인덱스의 경우 프라이머리키를 대체할 수도 있기에 대체키라고 함
저장 방식(알고리즘)별 인덱스
- B-tree
- 가장 일반적으로 쓰이는 인덱스
- 기존 칼럼의 값을 변형하지 않음
- Hash
- 칼럼값으로 해시값을 계산하여 인덱싱
- 기존 값을 변형해서 인덱싱하므로 값의 일부만 검색하거나 범위 검색시에는 사용할 수 없다
- 주로 메모리 기반의 데이터베이스에서 사용
데이터 중복 허용 여부
쿼리를 실행할 옵티마이저에게 항상 1건의 레코드 이후에 추가적인 업무를 해야할 지 말아야할지 결정하게 하기 때문에 큰 차이가 발생한다. 또한 인덱스로 인한 MySQL의 처리리 방식의 변화나 차이점이 상당히 많다.
- 유니크 인덱스
- 인덱스의 값이 유일
- 중복 인덱스 (Non-Unique)
- 인덱스의 값 중복을 허용
1.3 B-Tree 인덱스
B-Tree (Balanced tree)
1.3.1 구조 및 특성
인덱스의 키값은 정렬이 되어있지만 데이터 파일의 레코드 경우 정렬되지 않는다.
→ 데이터 파일 insert된 순서대로 저장되지 않고 삭제하여 빈공간이 생길경우 그 공간을 재활용하기 때문에 순서를 보장할 수 없다.
→ InnoDB의 경우 클러스터되어 디스크에 저장되므로 프라이머리 키 순서로 정렬되어 저장된다.
스토리지 엔진에 따른 구조 차이
- MyISAM
- ROWID(물리적인 주소값)를 세컨데리 키로 가지고 있어 이를 통해 데이터에 접근
- InnoDB
- PK가 ROWID역할을 함
- PK값을 인덱스 키로 가지고 있는 B-Tree를 한번더 검색하여 데이터에 접근 (B-tree가 두개)
1.3.2 B-Tree 인덱스 키 추가 및 삭제
1.3.2.1 인덱스 키 추가
B-Tree에 저장될 때,
- 저장될 키 값의 위치 검색
- 위치가 결정 될 시 리프 노드에 저장
- 리프 노드가 꽉 차 있을 시 리프노드를 분리 → 상위 브랜치 노드까지 처리의 범위가 넓어짐
이러한 과정때문에 쓰기 작업에 비용이 더 많이 듬
- MyISAM
- Insert 문장이 실행 되면 즉시 새로운 키 값을 B-Tree 인덱스에 반영
- InnoDB
- 체인지 버퍼를 통해 인덱스 키 추가 작업을 지연시켜 나중에 처리
- 프라이머리 키, 유니크 키의 경우 인덱스 중복 체크가 필요하기 때문에 즉시 처리해야함
1.3.2.2 인덱스 키 삭제
삭제시 해당 리프 노드를 찾아 삭제 마크 작업을 함
이후 삭제 마킹된 인덱스 키 공간은 방치 혹은 재활용하여 사용
마킹 시 디스크 쓰기가 필요 → 디스크 I/O 작업이 필요
MySQL 5.5 이상의 버전에서는 InnoDB에서 지연 처리 가능
1.3.2.3 인덱스 키 변경
원래 키값을 삭제 후 새로운 키 값을 추가하는 형태로 처리됨 (삭제 후 추가)
InnoDB에서 지연 처리 가능
1.3.2.4 인덱스 키 검색
- 트리 탐색을 통해 인덱스를 루트 → 브랜치 → 리프 노드 순으로 비교 작업을 수행
- 100% 일치 또는 값의 앞부분만 일치하는 경우에 사용 가능
- 키의 뒷부분이나 키 값에 변형이 이루어진 경우 빠른 검색 불가능
- 함수나 연산을 통한 검색 혹은 정렬은 비효율적
- 단순 검색 혹은 비교 조건의 검색에서만 효율적으로 검색 가능
- InnoDB의 경우 검색 수행시 수행하는 인덱스를 잠근 후 테이블 레코드를 잠그는 방식으로 구현
1.3.3 B-Tree 인덱스 사용에 영향을 미치는 요소
1.3.3.1 인덱스 키 값의 크기
인덱스 페이지
- 디스크에 데이터를 저장하는 가장 기본 단위
- 루트, 브랜치, 리프 노드를 구분하는 기준 (단위)
이진 (Binary) 트리와 B(Balanced)-Tree의 차이점은 자식 노드의 개수가 가변적이라는 점
MySQL (5.7)은 기본값이 16KB (설정에 따라 4KB ~ 64KB)
페이지당 키의 개수:
N * 1024 / (n + k)
(N : 인덱스 페이지 크기, n : 키 크기, k : 자식노드주소 크기)
키값이 커질 경우 페이지당 키의 개수가 작아짐
- 같은 양의 레코드를 읽어야 할때 순회해야할 횟수가 늘어남
- 인덱스를 캐시해둘 영역의 공간이 제한적이기 때문에 캐시해둘 레코드 수가 적어짐
→ 효율이 적어짐
1.3.3.2 B-Tree 깊이
- 인덱스의 깊이를 제어할 수 있는 방법은 없으나 인덱스 키값의 d(깊이) 제곱에 반비례하여 저장할 수 있는 키값이 달라짐
N : 인덱스 페이지 크기, n : 키 크기, k : 자식노드주소 크기, 깊이 : d
총 키의 수 : (N * 1024 / (n+k))^d
≒ 1/n^d
즉 인덱스 키값이 커질 수 록 저장할 수 있는 키들의 개수가 적어지는 것은 물로 깊이가 깊어지기 때문에 더 많은 디스크 읽기가 발생하게 되는 것이다
결론, 인덱스 키값의 크기는 되도록이면 작게 하는 것이 좋다
1.3.3.3 선택도(기수성)
선택도 (Selectivity) == 기수성 (Cardinality)
모든 인덱스 키 값 중 유니크한 값의 갯수 - N/n
N : 총 인덱스 개수, n : 유니크한 값
중복된 값 ▼ 유니크한 값 ▲ 기수성 ▲ 검색대상 ▼ 처리속도 ▲
N : 총 인덱스 개수, n : 유니크한 인덱스
책의 예시 참고 잘 나와있음
요약하자면 인덱싱을 통해 데이터를 조회할때 N/n
건을 조회해야 하는데 그중 필요한 것은 1건이므로 N/n-1
번의 불필요한 데이터를 조회하게 됨, 따라서 유니크한 값이 늘어날 수록 불필요한 데이터 검색이 줄어들고 그만큼 효율적으로 검색을 할 수 있게됨
1.3.3.4 읽어야 하는 레코드 의 건수
일반적으로 인덱스를 통해 레코드를 읽는 것이 그렇지 않은 것보다 높은 비용이 들게 됨
MySQL의 코스트 모델에서 전자의 경우가 건당 4~5배의 비용이 드는 것으로 예측
→ 인덱스를 통해 읽어야할 레코드 수가 전체 레코드의 20~25% 미만일 경우 인덱스를 사용한 방식이 효율적
→ 그 이상은 테이블을 모두 읽는 것이 효율적
20~25%가 인덱싱을 통한 손익분기점이다.
1.3.4 B-Tree 인덱스를 통한 데이터 읽기
1.3.4.1 인덱스 레인지 스캔
- 검색해야할 인덱스의 범위가 정해졌을 때 사용하는 방식
- 범위의 시작점을 찾기위해선 루트 → 브랜치 → 리프를 전부 들어가야함
- 가장 대표적인 접근 방식
- 인덱스 풀 스캔, 루스 인덱스 스캔에 비해 빠른 방법
- 해당 인덱스를 구성하는 칼럼의 정순 혹은 역순으로 정렬된 상태로 레코드를 반환
- 조건에 일치하는 리프 노드 1건당 1번의 랜덤 I/O가 일어남
- 20~25%의 레코드 읽기 손익분기점을 생각해야함
- 과정
- 인덱스 탐색 : 조건에 맞는 인덱스를 찾음
- 인덱스 스캔 : 탐색된 위치로 부터 인덱스를 차례대로 읽음
- 레코드 읽기 : 인덱스 스캔으로 부터 얻은 레코드 주소를 통해 레코드가 저장된 페이지를 가져오고 읽음
3.은 쿼리가 필요로 하는 데이터에 따라 필요없을 수 있음 (커버링 인덱스)
(findPKTop10이런건가?)
1.3.4.2 인덱스 풀 스캔
- 인덱스 전부를 읽는 방식
- 쿼리의 조건절에서 사용된 컬럼이 첫번째 칼럼이 아닌 경우 사용
- 인덱스 레인지 스캔보다는 느리지만 테이블 풀 스캔보다는 빠름
- 인덱스에 포함된 칼럼만으로 쿼리를 처리할 수 있는 경우 테이블 레코드를 읽을 필요가 없음
- 인덱스의 전체 크기가 테이블 자체보다는 작기 때문
- 인덱스를 전부 읽은 시점에서 인덱스를 효율적으로 사용했다고 할 수는 없다
1.3.4.3 루스 인덱스 스캔
- 오라클과 같은 DBMS의 인덱스 스킵 스캔과 비슷하게 작동
- 중간 중간 필요치 않은 인덱스 키 값은 무시하고 넘어감
- Group BY 나 집합 함수 중 MAX() 나 MIN()의 최적화하는 경우에 사용
1.3.4.4 인덱스 스킵 스캔
인덱스는 정렬의 기준이 되는 구성하는 칼럼의 순서가 중요
선행되는 칼럼을 사용하지 않는 쿼리의 경우 비효율적으로 작용됨
- MySQL 1.0부터 인덱스 스킵 스캔 최적화
- 루스 인덱스 스캔의 경우 GROUP BY에서만 적용가능
선행되는 칼럼의 유니크한 값을 모두 조회하고 유니크 한 값마다 칼럼 조건을 추가하여 다시 실행
ex)
ALTER TABLE example ADD INDEX ix_a_b (a, b);
SELECT a,b FROM example WHERE b >= 10;
→ 이 경우 인덱스를 사용 못함
→ 테이블 풀 스캔이 실행됨
SELECT a,b FROM example WHERE a = 10 and b >= 10
→ 이 경우 인덱스를 사용하게 됨이때 스킵 스캔의 설정을 키고 첫번째 경우를 다시 실행할 경우
a의 유니크 값이 10과 20 두가지라고 했을때
SELECT a,b FROM example WHERE a = 10 b >= 10;
SELECT a,b FROM example WHERE a = 20 b >= 10;
두개의 쿼리를 실행하는 것과 비슷한 형태 루스 인덱스 스캔과 동일한 방식으로 실행되게 된다.
단점
WHERE 조건절에 조건이 없는 인덱스의 선행 칼럼의 유니크한 값이 적어야함
→ 유니크한 값당 쿼리를 다시 실행하므로
쿼리가 인덱스에 존재하는 칼럼으로 처리 가능해야 함 (커버링 인덱스)
→ 인덱스 칼럼이 아닐 경우 풀 테이블 스캔으로 넘어감
1.3.5 다중 칼럼 인덱스
- 두개 이상의 칼럼을 포함하는 인덱스
- 다중 칼럼 인덱스 혹은 복합 칼럼 인덱스, Concatenated Index 라고도 함
- 선행 칼럼을 우선으로 정렬, 즉 후행의 경우 선행이 같은 경우에만 정렬이 됨
1.3.6 B-Tree 인덱스 정렬 및 스캔
- 스캔의 방향은 항상 정렬 방향과 같지는 않음
- 스캔 방향은 쿼리에 따라 옵티마이저가 실시간으로 실행 계획을 짦
1.3.6.1 인덱스의 정렬
1.3.6.1.1 인덱스 스캔 방향
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- ex) 정순에서 끝값을 찾을때 옵티마이저는 역순으로 조회하여 제일 처음의 값을 가져오도록 최적화 ##### 1.3.6.2 내림차순 인덱스
- 내림차순 인덱스 스캔이 오름차순 인덱스 스캔보다 느림
- 페이지 잠금이 인덱스 정순 스캔에 적합
- InnoDB의 경우 데드락을 방지하기 위해 정순 방향으로 페이지를 잠금
→ 정순의 경우 간단하지만 역순의 경우 복잡해짐 (트랜잭션을 새로 열어서 현 상황을 저장하고 작업을 재개)
- 페이지 내 인덱스 레코드가 단반향 구조
참고 : [https://tech.kakao.com/2011/06/19/mysql-ascending-index-vs-descending-index/](https://tech.kakao.com/2018/06/19/mysql-ascending-index-vs-descending-index/)
- 쿼리가 인덱스의 한반향에 몰려있어 특정 페이지의 병목 현상이 일어날 수 있을때 내림차순이 더 효과적
1.3.7 B-Tree 인덱스의 가용성과 효율성
WHERE
, GROUP BY
, ORDER BY
절의 경우 상황에 따라 인덱스를 사용할 수 도 있고 아닐 수 있음
1.3.7.1 비교 조건의 종류와 효율성
다중 칼럼 인덱스에서 각 칼럼의 순서와 그 칼럼에 사용된 조건이 동등 비교(
=
)와 부등호 (<
>
) 에 따라 달라짐ex)
SELECT * FROM example WHERE a = 1 AND b ≥ 1;
CASE 1 :
INDEX (a, b)
CASE 2 :
INDEX (b, a)
1번의 경우
a = 1 and b >= 1
인 레코드를 찾고 그 a = 1 이 아닐때 까지 읽어옴→ 우선적으로 a = 1 인 것들끼리 모여있으므로
2번의 경우
a = 1 and b >= 1
인 레코드를 찾고 그 중에서 a = 1 인지 비교해야함→ 우선적으로 b ≥ 1 인 것들끼리 모여있으므로
필터링 : 레코드가 나머지 조건에 맞는지 비교하면서 취사선택하는 작업
해당 쿼리에 대하여 작업 범위를 정해주지 못하고 다시 필터링이 필요한 경우 효율적이지 못하게 됨
- 필터링 조건 (체크 조건): 비교 작업의 범위를 줄이지 못하고 필터링이 필요하게 되는 조건
- 작업 범위 결정 조건 (공식 명칭 x) : 비교 작업의 범위를 줄여주는 조건
칼럼의 순서가 중요하다
1.3.7.2 인덱스의 가용성
- 다중 칼럼 인덱스에서 선행 칼럼 조건없이 후행 칼럼 조건만으로는 효율적인 인덱스 사용이 불가능
1.3.7.3 가용성과 효율성 판단
- 작업 범위 결정 조건으로 사용할 수 없는 경우
- NOT_EQUAL로 비교 (
<>
NOT IN
NOT BETWEEN
IS NOT NULL
)- 참고) 일반적인 DBMS에서는 인덱스에 null이 못들어가지만 MySQL에서는 들어감
- LIKE ‘%??’ 앞부분이 아닌 뒷부분 일치 형태의 문자열 패턴
- 스토어드 함수나 다른 연산자로 인덱스 칼럼이 변형된 후 비교
- NOT-DETERMINISTIC 속성의 스토어 함수가 비교 조건에 사용된 경우
- 데이터 타입이 서로 다른 비교
- 문자열 데이터 타입의 콜레이션이 다른 경우
- NOT_EQUAL로 비교 (
1.4 R-Tree 인덱스
1.4.1 구조 및 특성
- MBR : 최소 경계 상자 (Minimum Bounding Rectangle) → 도형을 감싸고 있는 최소크기의 상자
- 이때 각 상자들을 감쌀수 있는 상자들이 생기는데 이때 감싸고있는 정도에 따라 레벨이 나뉨
- 최상위 레벨 → 루트 노드
- 차상위 레벨 → 브랜치 노드
- 최하위 레벨 → 리프 노드
- 이때 R-Tree 란 이 사각형들의 포함 관계를 B-Tree 형태로 구현한 것을 의미함
1.4.2 R-Tree 인덱스의 용도
- GPS (위도, 경도), CAD/CAM 소프트웨어 등
1.5 전문 검색 인덱스 (이거 재미있어 보여요)
- B-Tree의 경우 좌측 일부 일치 혹은 전체 일치의 경우에만 검색이 가능
- 문서 내용 전체의 특정 키워드 검색에 특화된 알고리즘
1.5.1 인덱스 알고리즘
- 본문 내용에서 사용자가 검색할 키워드를 분석하고 이를 인덱스로 구축
- 키워드를 분석하는 기법에 따라 분석 알고리즘이 구분됨
1.5.1.1 어근 분석 알고리즘
MySQL 서버 전문 검색 인덱스 작업
- 불용어 처리
- 가치 없는 단어를 필터링 후 제거
- 불용어 소스코드가 정의 되어있으며 사용자가 설정 할 수도 있다
- 어근 분석
- 선정된 단어의 원형을 찾는 작업 → MySQL은 MeCab을 사용
- 각 용어에 맞게 사용하려면 많은 시간과 노력이 필요
1.5.1.2 n-gram 알고리즘
- 어근 분석의 단점을 보안하기 위해 도입
- n 글자씩 잘라서 인덱싱 하는 방법 (2-gram이 가장 대표적) (왜 중복은 이야기 안하시죠?), (인덱스 생성되는 과정도 말씀해주셔야죠) (술만 마시지 말구여)
- 인덱싱 이후 불용어처리를 하여 인덱싱을 함
1.5.1.3 불용어 변경 및 삭제
기본적으로 주어지는 불용어 소스코드의 경우 오히려 사용자를 더 혼란스럽게 할 수 있기 때문에 불용어를 직접 등록하는 것을 권장
→ 방법은 책 참고 (마법의 단어)
1.5.2 전문 검색 인덱스의 가용성
전문 검색 인덱스를 사용하기 위해선 다음 조건을 만족해야함
- 쿼리 문장이 전문 검색을 위한 문법 (MATCH ~ AGAINST ~)
- 테이블이 전문 검색 대상 칼럼에 대해서 전문 인덱스 보유
1.6 함수 기반 인덱스
칼럼 값을 변형해서 만들어진 값에 대해 인덱스를 구축해야할때 사용
실제 구조는 B-Tree와 동일
1.6.1 가상 칼럼을 이용한 인덱스
- 가상 컬럼을 추가하여 그 컬럼을 인덱스로 설정하여 인덱스를 생성
- 실제 테이블 구조가 변경된다는 단점
1.6.2 함수를 이용한 인덱스 (예시좀 넣어줘요) (사진을 못찾았어 그걸못봤어) 아아…..ㅇㅈ 그러니까 이걸 예시를 넣었어야지 미안해 알러뷰
- MySQL 1.0부터 테이블의 구조를 변경하지 않고 직접 함수를 사용한 인덱스를 생성 가능
- 사용시 조건절에 인덱스에 사용한 함수를 그대로 사용해야함
- 옵티마이저가 다른 표현식으로 간주하여 함수 기반 인덱스를 사용하지 못할 수도있음
1.7 멀티 밸류 인덱스 (Hongo 아닌가요?)
- 전문 검색 인덱스를 제외한 모든 인덱스는 레코드 1건 당 1개의 인덱스키를 가짐
- 멀티 벨류 인덱스 : 하나의 레코드에 여러개의 인덱스키를 가진 인덱스
- 정규화에 위배되는 형태
- JSON의 배열 타입 필드에 대한 인덱스
- 다음 함수를 사용해야 옵티마이저가 실행계획을 수립
- MEMBER OF()
- JSON_CONTAINS()
- JSON_OVERLAPS()
1.8 클러스터링 인덱스
- 프라이머리 키를 기준으로 비슷한 값 끼리 묶어서 저장
- InnoDB 스토리지 엔진에서만 지원
1.8.1 클러스터링 인덱스
- 테이블의 프라이머리 키에 대해서만 적용
- 프라이머리 키가 변경될 경우 그 레코드의 물리적 위치도 변경
- InnoDB에서는 PK가 ROWID(레코드가 저장된 물리적 주소)역할을 하기 때문
- 클러스터링 인덱스는 PK에 의해 저장 위치가 결정
- 클러스터링 인덱스의 리프노드에는 레코드이 모든 칼럼이 저장됨
- PK가 없는 InnoDB의 경우
- 클러스터링 키 선택의 우선순위
- PK가 있을 경우 PK를 우선
- NOT NULL 옵션의 유니크 인덱스 중 첫번쨰 인덱스를 선택
- 자동으로 유니크한 값을 가지도록 증가되는 칼럼을 내부적으로 추가한후 선택
- 3.의 경우 사용자에게 PK가 노출 되지 않으며 아무의미 없는 값으로 클러스터링 되므로 어떠한 해택도 없음 → PK를 명시하는 것이 좋음
- 클러스터링 키 선택의 우선순위
1.8.2 세컨더리 인덱스에 미치는 영향
- 모든 세컨더리 인덱스에는 PK 값이 저장되어 있음
1.8.3 클러스터링 인덱스의 장점과 단점
장점 | - PK 검색시 그속도가 매우 빠름 |
인덱스로만 처리된 수 있는 경우가 많음 — — 단점 - 클러스터링 키 값이 커질경우 전체적으로 크기가 커짐 - 세컨더리 인덱스를 통한 검색시 처리 성능이 느림
- INSERT할 때 처리 성능이 느림
PK 변경시 레코드를 DELETE하고 INSERT하는 작업이 필요
1.8.4 클러스터링 테이블 사용 시 주의사항
1.8.4.1 클러스터링 인덱스 키의 크기
- 모든 세컨더리 인덱스가 PK를 값을 포함함
- 클러스터링 인덱스 키의 크기가 커질 경우 급격하게 전체 인덱스의 크기가 커짐
1.8.4.2 프라이머리 키는 AUTO-INCREMENT 보다는 업무적인 칼럼으로 생성 ( 가능한 경우)
- MyISAM과는 다르게 InnoDB에서는 무엇을 PK로 선택하느냐에 따라서 그 성능차이가 많이남
- 칼럼의 크기가 크더라도 해당 레코드를 대표할 수 잇는 칼럼을 PK로 설정해야함
1.8.4.3 프라이머리 키는 반드시 명시할 것
- PK가 명시되지 않을 경우 의미없는 AI 칼럼을 생성하고, 클러스터링의 장점을 볼 수 없기때문에 명시하는 것이 좋음
1.8.4.4 AUTO-INCREMENT 칼럼을 인조 식별자로 사용할 경우
- 여러개의 칼럼으로 복합적인 PK를 만들 경우
- 세컨더리 인덱스가 필요치 않을 경우 그대로 씀
- 세컨더리 인덱스가 필요하고 PK의 크기가 클경우 AUTO__INCREMENT칼럼을 추하여 프라이머리 키로 설정 → 인조 식별자(Surroget Key)
- 로그 테이블과 같은 INSERT 위주의 테이블 들은 AUTO_INCERMENT를 이용한 인조 식별자 PK가 성능 향상에 좋음
1.9 유니크 인덱스
- MySQL에서는 인덱스 없이 유니크 제약만 설정할 방법이 없음
- 유니크 인덱스에서는 NULL값 저장 가능
- NULL은 특정 값이 아니므로 2개 이상 저장 가능
- MyISAM에서는 NULL이 허용되
1.9.1 유니크 인덱스와 일반 세컨더리 인덱스의 비교
유니크한 인덱스와 그렇지 않은 일반 세컨더리 인덱스는 구조상 차이가 없음
1.9.1.1 인덱스 읽기
읽기에서 성능차는 미미함
중복된 값이 허용되므로 읽어야 할 레코드가 많아서 느린것
→ 인덱스 자체의 성능때문에 느린것은 아님
1.9.1.2 인덱스 쓰기
- 유니크 인덱스의 쓰기 작업에서는 중복값 체크 과정이 추가됨
중복값 체크 시 읽기 잠금, 쓰기 작업 시 쓰기 잠금
→ 데드락이 자주 발생
InnoDB의 체인지 버퍼가 사용됨
1.9.2 유니크 인덱스 사용 시 주의사항
- 유일성이 보장되어야 하는 칼럼에서만 유니크 인덱스를 생성 그렇지 않다면 세컨더리 인덱스를 생성하는 방법을 고려해볼것
1.10 외래키
- MySQL에서 외래키는 InnoDB에서만 생성 가능
- 외래키 제약 설정 시 자동으로 연관된 테이블에 인덱스가 생성
- InnoDB의 외래키 관리
- 테이블의 변경(쓰기 잠금)이 발생하는 경우에만 잠금 경합(잠금 대기)이 발생
- 외래키와 연관되지 않은 칼럼의 변경은 최대한 잠금 경합(잠금 대기)을 발생 시키지 않음
1.10.1 자식 테이블의 변경이 대기하는 경우
- 자식 테이블의 외래 키 칼럼 변경은 부모 테이블의 확인이 필요
- 부모 테이블의 해당 레코드가 쓰기 잠금이 걸려 있을 경우 자식 테이블은 대기
- 자식 테이블의 외레키가 아닌 칼럼은 잠금 확장이 일어나지 않음
1.10.2 부모 테이블의 변경 작업이 대기하는 경우
자식테이블이 생성될 때 정의된 외래키의 특성(ON DELETE CASCADE)에 의해 부모 테이블의 변경시 자식 테이블의 확인이 일어남
물리적으로 외래키를 생성시 자식 테이블에 레코드가 추가되는 경우 부모 테이블을 확인해야함
→ 이때 체크작업이 일어나면서 읽기 잠금이 걸림