8장 인덱스
8.4 R-Tree 인덱스
2차원 공간 개념의 값 인덱스다. MySQL 의 공간 확장에는 세 가지 기능이 포함되어 있다.
- 공간 데이터를 저장할 수 있는 데이터 타입
- 공간 데이터의 검색을 위한 공간 인덱스 (R-Tree 알고리즘)
- 공간 데이터의 연산 함수 (거리 또는 포함 관계의 처리)
구조 및 특성
대표적으로 Point, Line, Polygon, Geometry 타입을 지원한다. 마지막 Geometry 타입은 나머지 타입의 슈퍼 타입으로 모두 저장할 수 있다.
R-Tree 알고리즘을 이해하기 위해서는 MBR 이라는 개념이 필요하다. MBR 은 Minimum Bounding Rectangle 로 해당 도형들을 감싸는 최소 크기의 사각형을 의미한다.
[5-20] 같은 도형 (공간 데이터) 가 있다고 해보자. 이 도형들을 3개의 MBR 레벨로 나눠 그려볼 수 있다.
최 상위에는 R1, R2 가 존재하며 최하위에는 R7~R14 인 개별 요소들이 들어간다.
마지막으로 각 객체인 최하위들은 인덱스와 동일하게 리프 노드에 저장된다.
R-Tree 인덱스의 용도
MBR 정보를 통해 B-Tree 형태를 구축하기에 R-Tree 라는 이름을 가지고 있으며, 공간 인덱스라고도 한다.
R-Tree 는 각 도형의 포함 관계를 이용해 만들어진 인덱스이다. 포함관계를 비교하는 함수들을 통해 검색하는 경우에 해당 인덱스를 이용할 수 있다. 대표적으로 사용자의 위치로부터 5KM 내 음식점 검색 등에 사용할 수 있다.
추가로 ST_Distance_Sphere() 함수는 공간 인덱스를 효율적으로 사용하지 못하기에 ST_Within(), ST_Conains() 를 이용해 거리 기반 검색을 해야한다.
사용자로부터 5KM를 검색하기 위해서는 원으로 검색해야하지만 MBR 로 비교를 수행해야하기에 해당 원을 포함하는 최소 사각형으로 비교해야한다.
8.5 전문 검색 인덱스
MySQL 의 B-Tree 인덱스는 실제 칼럼의 값이 1MB 더라도 1,000(MyISAM) 바이트 또는 3,072(InnoDB) 바이트까지만 잘라서 인덱스 키로 사용한다. 또한 B-Tree 인덱스는 전체 일치 또는 좌측 일부 일치와 같은 검색만 가능하다.
문서의 내용 전체를 인덱스화해서 특정 키워드가 포함된 문서를 검색하는 전문 검색에는 일반적인 B-Tree 인덱스를 사용할 수 없다. 문서 전체에 대한 분석과 검색을 위한 이런 인덱싱 알고리즘을 전문 검색 인덱스라 한다.
인덱스 알고리즘
전문 검색에서는 키워드를 분석하고 빠른 검색을 위해 이러한 키워드로 인덱스를 구축한다.
이를 위해 두 가지 중요한 처리 과정이 있다.
- 불용어(Stop Word) 처리
- 어근 분석(Stemming)
불용어 처리는 별 가치 없는 단어를 필터링해 제거하는 작업이다. 개수가 많지 않아 상수로 정의하기도 하며, 불용어 데이터베이스를 생성하기도 한다.
어근 분석은 검색어로 선정된 단어의 뿌리인 원형을 찾는 작업이다. MySQL 에서는 오픈소스 형태소 분석 라이브러리인 MeCab 을 플러그인으로 사용할 수 있다.
n-gram 알고리즘
본문을 무조건 몇 글자씩 잘라서 인덱싱하는 방법이다. 형태소 분석보다는 알고리즘이 단순하고 만들어진 인덱스의 크기는 상당히 크다. n 은 인덱싱할 키워드의 최소 글자수를 의미하는데, 일반적으로 2-gram 이 많이 사용된다.
다음 예시로 2-gram 알고리즘을 적용시켜보자
To be or not to be. That is the question.
각 단어는 띄어쓰기와 마침표를 기준으로 10개의 단어로 구분되고, 2글자씩 중첩해서 토큰으로 분리된다. 중첩이기 때문에 10글자의 단어는 5개의 토큰이 아닌 9개의 토큰으로 구분된다. 이렇게 구분된 토큰을 인덱스에 저장하기만 하면 된다.
MySQL 은 이 토큰들 중 불용어를 걸러내는데, 동일하거나 불용어를 포함하는 경우 걸러 버린다. 최종적으로 불용어 일치, 포함 토큰을 제외한 토큰들이 인덱스에 등록된다.
이러한 불용어들은 사용자가 직접 등록도 가능하다.
전문 검색 인덱스를 사용하기 위해서는 두 가지 조건을 갖춰야한다.
- 쿼리 문장이 전문 검색을 위한 문법 (MATCH ... AGAINST ... ) 을 사용
- 테이블이 전문 검색 대상 칼럼에 대해서 전문 인덱스 보유
8.6 함수 기반 인덱스
때로는 칼럼의 값을 변형해서 만들어진 값에 대해 인덱스를 구축해야할 때가 있는데, 이러한 경우 함수 기반의 인덱스를 활용한다. 구현 방법은 두 가지로 구분할 수 있다.
- 가상 칼럼을 이용한 인덱스
- 함수를 이용한 인덱스
인덱싱할 값을 계산하는 과정의 차이만 있을 뿐, 실제 내부적 구조 및 유지관리 방법은 B-Tree 와 동일하다.
first_name 과 last_name 을 합쳐서 검색해야하는 요건이 있다면, full_name 이라는 가상 칼럼을 추가하고, 인덱스를 생성할 수 있다. 이는 테이블에 새로운 칼럼을 추가하는 것과 같은 효과를 내기에 실제 테이블 구조가 변경된다.
실제 테이블 구조를 변경하지 않고 함수를 사용해 인덱스를 생성할 수 있다. 계산된 결과값의 검색을 빠르게 만들어줄 수 있다. 이를 제대로 활용하기위해 조건절에 함수 기반 인덱스에 명시된 표현식이 그대로 사용되어야한다. 만약 표현식이 다르다면 옵티마이저는 다른 표현식으로 간주해 함수 기반 인덱스를 사용하지 못한다.
8.7 멀티 밸류 인덱스
전문 검색 인덱스를 제외한 모든 인덱스는 레코드 1건이 1개의 인덱스 키값을 가진다. 하지만 멀티 밸류 인덱스는 하나의 레코드가 여러 개의 키값을 가질 수 있다. 일반적으로 정규화에 위배되는 형태이다. 하지만 최근 JSON 데이터 타입을 지원하면서 JSON 타입의 필드에 저장된 원소들에 대한 인덱스 요건이 발생했다.
MongoDB 가 지원하던 형태의 인덱스이다. 이를 활용하기 위해 일반적인 조건 방식이 아닌 반드시 함수들을 사용해서 검색해야 인덱스를 활용한 실행 계획이 수립된다.
- MEMBER OF()
- JSON_CONTAINS()
- JSON_OVERLAPS()
8.8 클러스터링 인덱스
클러스터링은 테이블의 레코드를 비슷한 것들끼리 묶어서 저장하는 형태로 구현된다. 이는 주로 비슷한 값들을 동시에 조회하는 경우가 많기 때문이다.
대게 프라이머리 키에 대해서만 적용되는 내용이다. 여기서 중요한 것은 프라이머리 키 값에 의해 레코드의 저장 위치가 결정된다는 것이다. 또한 프라이머리 키 값이 변경되면 물리적인 저장 위치가 바뀌어야한다. 따라서 인덱스 알고리즘이라기보다 테이블 레코드의 저장 방식이라 볼 수 있다.
테이블 구조 자체는 B-Tree 와 유사하지만 리프 노드에서는 레코드의 모든 칼럼이 저장되어있다. 즉, 클러스터링 인덱스는 테이블 그 자체가 하나의 인덱스 구조로 관리되는 것이다.
만약 프라이머리 키가 없으면 어떻게 구성할까?
- 프라미어리 키가 있으면 기본적으로 해당 키를 클러스터링 키로 선택
- NOT NULL 옵션의 유니크 인덱스 중 첫 번째를 선택
- 자동으로 유니크한 값을 가지도록 증가되는 칼럼을 내부적으로 추가해 선택
세컨더리 인덱스에서는 레코드의 주솟값을 가지고 있다면 클러스터링 키 값이 변경될 때마다 모든 인덱스에 저장된 주솟값을 변경해줘야한다. 이를 위해 모든 세컨더리 레코드는 주소가 아닌 프라이머리 키 값을 저장하도록 되어있다.
장점
- 프라이머리 키로 검색 시 매우 빠름
- 테이블의 모든 세컨더리 인덱스가 프라이머리 키를 가지고 있으므로 인덱스만으로 처리할 수 있는 경우가 많다.
단점
- 테이블의 모든 세컨더리 인덱스가 클러스터링 키를 갖기에 클러스터링 키의 크기가 크면 전체적인 인덱스 크기가 커진다.
- 세컨더리 인덱스를 통해 검색할 경우에도 클러스터링 키를 통한 검색이 필요하다.
- INSERT 할 때 프라이머리 키에 의해 레코드 저장 위치가 결정되기에 성능이 느리다.
- 프라이머리 키를 변경할 때 레코드를 삭제하고 삽입하는 작업이 필요하기에 성능이 느리다.
8.9 유니크 인덱스
인덱스라기보다 제약 조건에 가깝다. 테이블이나 인덱스에 같은 값이 2개 이상 저장될 수 없음을 의미한다.
인덱스를 읽을 때에는 디스크가 아닌 CPU 에서 칼럼값을 비교하기에 성능상 차이가 거의 없다.
인덱스를 쓸 때에는 유니크 인덱스의 경우 중복된 값 여부를 체크하는 과정이 필요하다. 이때 데드락이 아주 빈번히 발생한다.
8.10 외래키
외래키 제약은 InnoDB 스토리지 엔진에서만 생성할 수 있으며, 자동으로 연관되는 테이블의 칼럼에 인덱스까지 생성된다.
InnoDB 의 외래키 관리에는 중요한 두 가지 특징이 있다.
- 테이블 변경이 발생하는 경우 잠금 경합이 발생한다.
- 외래키와 연관되지 않은 칼럼의 변경은 최대한 잠금 경합을 발생시키지 않는다.
자식 테이블이 변경될 때 부모 테이블의 해당 레코드가 쓰기 잠금이 걸려있다면 잠금 대기를 발생시킨다.
자식 테이블을 변경하기 위해 레코드 잠금이 걸린 상태에서 부모 테이블에서 레코드를 삭제시킨다면 연관된 자식 레코드도 삭제되어야한다. 이때에도 자식 테이블 레코드가 잠금이 걸려있기에 해제될 때까지 기다려야한다.
'etc > Book' 카테고리의 다른 글
[Real MySQL 8.0] 9장 옵티마이저와 힌트 (2) (8일차) (1) | 2024.11.01 |
---|---|
[Real MySQL 8.0] 9장 옵티마이저와 힌트 (1) (7일차) (0) | 2024.10.31 |
[Real MySQL 8.0] 7장 데이터 암호화, 8장 인덱스 (1) (5일차) (1) | 2024.10.30 |
[Real MySQL 8.0] 5장 트랜잭션과 잠금, 6장 데이터 압축 (4일차) (2) | 2024.10.28 |
[Real MySQL 8.0] 4장 아키텍처 (2) (3일차) (0) | 2024.10.26 |