

읽어야하는 데이터가 연속적으로 있어 쭉 읽기만 하는 것
하드 디스크 드라이브의 플래터를 돌려서 읽어야 할 데이터가 저장된 위치로 디스크 헤더를 이동시킨 다음 데이터를 읽는 것
디스크의 성능은 디스크 헤더의 위치 이동 없이 얼마나 많은 데이터를 한번에 기록하느냐에 따라 결정된다. 따라서, 랜덤 I/O 작업이 작업 부하가 훨씬 크다는 것을 알 수 있다.
데이터베이스의 성능 튜닝은 랜덤 I/O 를 줄이는 것이다.
랜덤 I/O를 줄인다는 것은 꼭 필요한 데이터만 읽도록 쿼리를 개선하는 것을 의미한다.


인덱스: 책의 맨 끝에 있는 찾아보기
페이지 번호: 데이터 파일에 저장된 레코드의 주소
책의 내용: 데이터 파일

DBMS의 인덱스의 자료 구조 : SortedList
SortedList의 장단점은?
B(Balanced)-Tree 는 DB에서 가장 일반적으로 사용되는 인덱스 알고리즘이다.
최상위 : 루트 노드
중간 노드 : 브랜치 노드
가장 하위에 있는 노드 : 리프 노드
InnoDB 사용하는 구조

세컨더리 인덱스는 프라이머리 키를 주소처럼 사용
따라서 레코드를 읽을 때, 데이터 레코드를 바로 찾지 않고 다시 한 번 프라이머리 키 인덱스의 루트 노드로 가는 모습을 볼 수 있다.

비클러스터형 : 컬럼은 정렬되지만 실제 데이터는 정렬되지 않고 물리적으로 저장하지도 않음

클러스터형 : 데이터와 함께 전체 테이블이 물리적으로 정렬된다

클러스터형 인덱스와 비클러스터형 인덱스의 혼합 : 비클러스터형 인덱스를 먼저 거치고, 이어 클러스터형 인덱스를 거쳐 데이터를 찾는다.
테이블의 레코드를 저장하거나 변경하는 경우 인덱스 키 추가나 삭제 작업이 발생
인덱스 추가로 인해 테이블에 레코드를 추가하는 작업의 비용 1이라고 하면
인덱스에 키를 추가하는 비용은 1.5로 예측 가능
레코드 추가 작업 + 인덱스 키 추가 작업 으로 인해 쓰기,수정,삭제 작업에 리소스가 더 많이 할당된다!
인덱스를 추가하고 정렬하는데 드는 리소스를 감수하고 인덱스를 쓰는 이유는 바로 조회 성능 때문이다.
b-tree 검색시 주의점:
ex) 이름이 “yeob”인 사람 검색 → 인덱스 O
ex) 이름이 “ye”로 시작하는 사람 검색 → 인덱스 O

- 디스크에 데이터를 저장하는 가장 기본 단위를 “**페이지**” 라 하고 인덱스도 페이지 단위로 관리된다.
- **인덱스 키의 크기가 커지면 전체적인 인덱스의 크기도 커진다.**
→ 메모리에 캐시해 둘 수 있는 레코드 수는 줄어들어 성능이 저하될 수 있다.

- **인덱스 키 값의 크기가 커지면 페이지가 담을 수 있는 키 값의 개수가 적어지고, 같은 레코드 건수여도 깊이가 깊어져 디스크 읽기가 더 많이 필요**합니다.
Mysql이 어떻게 인덱스를 이용해 실제 레코드를 읽어오는지 살펴 보자.
인덱스 레인지 스캔

인덱스 풀 스캔

루스 인덱스 스캔

인덱스 스킵 스캔

만약 한 테이블에 컬럼 두개에 인덱스를 (a,b) 순서대로 건다면 조건절에서 a,b 를 모두 사용한다면 인덱스를 쓰지만, b만 쓰면 인덱스를 사용할 수 없다.
그러나 Mysql 8.0 버전부터는 b만으로도 인덱스 검색이 가능하게 해주는 인덱스 스킵 스캔 최적화 기능이 도입
select gender, birth_date from employees where birth_date>='1965-02-01’ 위와 같은 쿼리를 사용할 때 gender 의 유니크 값을 찾아내어 내부적으로 아래 보이는 쿼리들을 실행하는 것과 비슷한 형태의 최적화를 실행하게 된다.
select gender, birth_date from employees where gender='M' and birth_date>='1965-02-01'
select gender, birth_date from employees where gender='F' and birth_date>='1965-02-01'

→ 따라서 다중 칼럼 인덱스는 각 칼럼의 순서가 굉장히 중요하다.
인덱스의 키 값은 오름차순 혹은 내림차순으로 정렬되는데, 이를 어느 방향으로 읽을지는 옵티마이저의 실행계획에 따라 결정된다.
MySQL 5.7 까지는 정렬 순서를 혼합해서 쓸 수 없었지만, 8.0 부터는 아래와 같은 형태로 정렬 순서를 혼합해 쓸 수 있다.(ASC DESC 혼합)
CREATE INDEX ix_teamname_userscore ON employees (team_name ASC, user_score DESC)
옵티마이저는 필요한 인덱스가 존재하는 위치에 따라, 인덱스를 정순으로 읽거나, 역순으로 읽을 수 있습니다. SELECT * FROM employees ORDER BY first_name DESC LIMIT 5 와 같이 테이블에 first_name에 정의된 인덱스를 역순으로 읽으면서 처음 5개만 가져오면 된다. 이는 옵티마이저가 최적의 실행계획을 만들어 준다.
결론적으로, 정순이든 역순이든 자주 사용되는 정렬 순서로 인덱스를 생성하는 것이 효율적이다.
어떤 조건에서 인덱스를 사용할 수 있고, 사용할 수 없게 되는지 알아보자
아래의 경우 인덱스를 사용할 수 없다.
WHERE column_i <> 'value'WHERE column_i LIKE '%value'WHERE column_i = '123' (column_i가 INT인 경우)→ 인덱스를 사용할 수 없는 다양한 경우들이 존재하므로 인덱스 사용 시 주의해야 한다.

공간 인덱스: 2차원 데이터를 인덱싱 하고 검색하는 목적으로 사용

대량의 텍스트 데이터를 효율적으로 검색할 수 있도록 만든 구조
주로 문서 검색, 웹 검색 엔진, 전자상거래 제품 검색, 로그 분석, 추천 시스템 등에서 활용
칼럼의 값을 변형해서 만들어진 값에 대한 인덱스
함수 기반 인덱스를 구현하는 방법 두 가지
가상 칼럼은 실제로 데이터를 저장하지 않고, 다른 칼럼의 값을 기반으로 계산된 값을 제공하는 칼럼
first_name, last_name 두 칼럼을 합쳐서 검색해야 하는 경우
기존 : full_name이라는 칼럼을 추가하고 모든 레코드에 대해 full_name을 업데이트하는 작업
가상 칼럼 :
ALTER TABLE user
ADD full_name VARCHAR(30) AS (CONCAT(first_name, ' ', last_name)) VIRTUAL,
ADD INDEX idx_fullname(full_name);
만약 테이블을 조회해본다면?
full_name 칼럼이 생성되며 해당 full_name 가상 칼럼은 데이터를 직접 저장하지 않고 first_name과 last_name의 값을 기반으로 동적으로 계산된 결과를 제공
단점 : 가상 칼럼은 테이블에 새로운 칼럼을 추가하는 것과 같은 효과를 내기 때문에 실제 테이블의 구조가 변경된다.
MySQL 8.0 버전부터는 테이블의 구조를 변경하지 않고, 함수를 직접 사용하는 인덱스 생성 가능
CREATE TABLE (
...
first_name VARCHAR(10),
last_name VARCHAR(10),
...
INDEX idx_fullname((CONCAT(first_name, ' ', last_name)))
);
SELECT * FROM 테이블명
WHERE CONCAT(first_name, ' ', last_name) = 'Kim DongWoo';하나의 레코드에 여러가지 키 값을 가질 수 있는 인덱스이다.
주로 JSON의 배열 타입의 필드에 저장된 원소들에 대한 인덱스를 구현하기 위해 사용

멀티 밸류 인덱스 이점:
JSON 데이터들을 미리 인덱싱하여 조회시, 필터링하고 필요한 정보만 가져옴으로써 응용 프로그램에서 처리하는 것보다 더 빠르고 효율적으로 처리 가능하다.

클러스터링 인덱스 : 프라이머리 키 를 기준으로 순서에 따라 물리적으로 정렬되어 저장된 인덱스 구조(InnoDB 에서는 pk 설정 시 자동으로 만들어짐, 한 테이블에 하나만 존재)


참고 : 프라이머리 키가 없다면?
내부적으로 대체키를 선정하여 클러스터링 인덱스를 생성하거나 자동으로 유니크한 값을 가지는 히든 칼럼을 추가하여 클러스터링 인덱스를 생성한다.
클러스터링 인덱스의 장점과 단점
DELETE하고 INSERT하는 작업이 필요하기 때문에 처리 성능이 느리다.유니크 제약조건 : 테이블이나 인덱스에 같은 값이 2개 이상 저장될 수 없음을 의미
유니크 제약조건을 걸면 내부적으로 유니크 인덱스가 생성된다.
CREATE TABLE test3 (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) UNIQUE,
...
);

세컨더리 인덱스와의 비교
그럼 왜쓰지 ??
핵심은 유니크 제약조건을 통해 데이터 무결성을 보장하는 것이다.(대신 성능은 안좋아짐)
또한 유니크 인덱스가 있으면 성능이 좋을거라는 생각을 버려야 겠다.
외래키 제약이 설정되면 자동으로 연관되는 테이블의 컬럼에 인덱스가 생성된다.
CREATE TABLE test4 (
id INT AUTO_INCREMENT PRIMARY KEY,
test_id INT,
...
FOREIGN KEY (test_id) REFERENCES test(id)
);

주요 두가지 특징
부모의 허락이 있을 때까지 기다림
UPDATE tb_child SET pid=2 WHERE id=100;
부모가 자식의 쓰기 잠금이 해제될 때까지 기다려야 한다.
DELETE FROM tb_parent WHERE id=1;
핵심은 외래 키가 물리적으로 생성되면 이처럼 연관 테이블에 읽기 잠금이 일어나기 때문에 쿼리 동시처리에 영향을 미치므로 이것을 고려해서 외래 키 모델링을 진행 해야 한다는 것이다.