[MYSQL] 인덱스(8.1 ~ 8.4)

최승훈·2023년 2월 1일
0

아래 내용은 Real MySQL 8.0 1권의 8장 일부분을 요약한 내용입니다.

8.1 디스크 읽기 방식

비록 최근에 자기 디스크 원판을 사용하는 하드디스크보다 SSD 드라이브가 많이 활용되고 있지만 그래도 여전히 데이터 저장 매체가 컴퓨터에서 가장 느린 부분이라는 사실은 변함이 없다. 데이터 베이스의 성능 튜닝은 어떻게 디스크 I/O를 줄이느냐가 관건일 때가 상당히 많다.

8.1.1 하드 디스크 드라이브(HDD)와 솔리드 스테이트 드라이브(SSD)

한 번에 많은 데이터를 읽는 순차 I/O에서는 SSD가 하드 디스크 드라이브(헤더를 움직이지 않을 떄)보다 조금 빠르거나 거의 비슷한 성능을 보이기도 한다. 하지만 SSD의 장점은 기존 하드 디스크 드라이브 보다 랜덤 I/O가 훨씬 빠르다는 것이다. 데이터 베이스 서버에서 순차 I/O 작업은 그다지 비중이 크지 않고 랜덤 I/O를 통해 작은 데이터를 읽고 쓰는 작업이 대부분이므로 SSD의 장점은 DBMS 스토리지 최적이라고 볼 수 있다.

8.1.2 랜덤 I/O아 순차 I/O

랜덤 I/O는 읽어야하는 데이터가 물리적으로 불연속적으로 있기 때문에 디스크 헤더를 이동 시킨 다음 데이터를 읽는 것을 의미한다. 이때 디스크 헤드를 이동시키는 시간을 Seek time이라고 한다. 순차 I/O는 읽어야하는 데이터가 연속적으로 있어 읽기만 하는 경우를 의미한다.
하드 디스크 드라이브(HDD)의 경우 모든 랜덤 I/O에서 데이터를 수집하기 위한 추가 Disk head 이동이 매우 시간이 많이 걸리기 때문에 성능이 더 저하된다. SSD를 사용하면 Disk head 이동의 단점이 아니라 스토리지 디바이스가 단일 I/O가 아닌 여러 I/O를 처리해야 하는 단점이 있다.

8.2 인덱스란?

인덱스를 실세계에 비유할 때 가장 많이 사용되는 모델이 책의 맨 뒤에 있는 찾아보기이다. 찾아보기가 인덱스라면 책의 내용은 데이터 파일이라고 볼 수 있다. 찾아보기의 페이지 번호는 데이터의 주소에 비유될 수 있다. DBMS도 모든 데이터를 검색해서 원하는 결과를 가져오려면 시간이 오래걸리는데 이러한 이유로 (컬럼 - 주소 쌍)을 인덱스로 만들어 두는 것이다. 책의 찾아보기와 인덱스의 공통점은 정렬이다. 찾아보기는 최대한 빨리 찾을 수 있게 ㄱ,ㄴ,ㄷ,ㄹ... 순으로 정렬되어있다. 인덱스도 마찬가지로 컬럼을 정렬해서 보관한다. 인덱스를 사용하면 INSERT, UPDATE, DELETE의 성능은 저하되고 대신 읽기 속도 성능이 좋아진다.

  • 역할
    • 프라이머리 키 : 말 그대로 프라이머리 키로 만들어진 인덱스, null을 허용하지 않고 중복을 허용하지 않는다.
    • 보조 키 : 프라이머리 키를 제외한 나머지 모든 인덱스
  • 데이터 저장 방식(알고리즘)
    • B-Tree 인덱스 : 가장 일반적인 인덱스 알고리즘, 컬럼의 값을 변형하지 않고 원래 값을 이용해 인덱싱
    • Hash 인덱스 : 해시값을 계산해서 인덱싱한다. 매우 빠른 검색을 지원한다.
  • 데이터 중복 허용 여부
    • Unique
    • Non-Unique
  • 인덱스의 기능별로 구분
    • 전문 검색용 인덱스
    • 공간 검색용 인덱스

8.3 B-Tree 인덱스

B-Tree는 DB 인덱싱 알고리즘 중에서 가장 많이 사용되는 인덱스 알고리즘이다. 일반적으로 DBMS에서는 B+-Tree또는 B-Tree가 사용된다. 참고로 B-Tree의 B는 Binary가 아니라 Balanced이다. B-Tree는 칼럼의 원래 값을 변형시키지 않고 인덱스 구조체 내에서는 항상 정렬된 상태로 유지한다. 전문 검색같은 특별한 경우를 제외한 대부분의 인덱스는 거의 B-Tree를 활용한다.

8.3.1 구조 및 특성

B-Tree는 트리 구조의 최상위에 하나의 루트 노드가 있고, 그 하위에 자식 노드가 붙어 있는 형태다. 최하위 노드를 리프 노드라고 하고, 중간의 노드들을 브랜치 노드라고 한다. 인덱스는 테이블의 키 칼럼만 갖고 있기 때문에 나머지 칼럼을 읽으려면 데이터 파일에서 해당 레코드를 찾아야 한다. 그렇기 때문에 인덱스의 리프 노드는 데이터 파일에 저장된 레코드의 주소를 갖고 있다.

MyISAM과 InnoDB 스토리지 엔진에서의 인덱스에서 가장 큰 차이는 세컨더리 인덱스를 통해 레코드를 찾아가는 방법이다. MyISAM 테이블세컨더리 인덱스물리적인 주소를 갖는 반면에 InnoDB 테이블은 프라이머리 키를 주소처럼 사용하기 때문에 논리적인 주소를 갖는다. 그래서 InnoDB 테이블에서 인덱스를 통해 레코드를 읽을 때는 인덱스에 저장되어 있는 프라이머리 키 값을 이용해 프라이머리 키 인덱스를 한 번 더 검색한 후 프라이머리 키 인덱스의 리프 페이지에 저장되어 있는 레코드를 읽어온다. 즉, 프라이머리 키를 저장하고 있는 B-Tree를 한 번 더 검색하는 것이다.

8.3.2 B-Tree 인덱스 키 추가 및 삭제

8.3.2.1 인덱스 키 추가

새로운 키 값이 추가될 때는 테이블의 스토리지 엔진에 따라 즉시 인덱스에 저장될 수도 있고 아닐 수도 있다. MyISAM이나 MEMORY 스토리지 엔진은 INSERT 문장이 실행되면 즉시 새로운 키 값을 인덱스에 추가한다. 반면에 InnoDB 테이블에서는 필요하다면 인덱스 키 추가 작업을 나중에 처리한다. 하지만 PK나 유니크 인덱스의 경우 중복 체크가 필요하기 때문에 즉시 반영한다.

8.3.2.2 인덱스 키 삭제

해당 키 값이 저장된 리프 노드를 찾아서 그냥 삭제 마크만 하면 끝이다. 삭제 마킹된 인덱스 키 공간은 그대로 방치하거나 재활용할 수 있다.

8.3.2.3 인덱스 키 변경

변경될 값에 따라 저장될 리프 노드가 결정되기 때문에 단순히 인덱스 상의 키 값만 변경하는 것은 불가능하다. 변경 작업은 먼저 키 값을 삭제 후, 다시 새로운 키 값을 추가하는 형태로 처리된다.

8.3.2.4 인덱스 키 검색

인덱스를 검색하는 작업은 B-Tree의 루트 노드부터 리프 노드까지 이동하면서 비교 작업을 수행하는데, 이 과정을 트리 탐색이라고 한다. 인덱스 트리 탐색은 SELECT 뿐만 아니라 레코드를 먼저 검색해야하는 UPDATE, DELETE 의 경우에도 사용할 수 있다.

B-Tree 인덱스를 이용한 검색은 100% 일치 또는 값의 앞부분만 일치하는 경우에 사용할 수 있다. 중요한 것은 인덱스의 키 값을 변형한 후에는 빠른 검색 기능을 이용할 수 없다는 것이다. 이미 변형된 값은 B-Tree 인덱스에 존재하는 값이 아니기 때문에, 함수나 연산을 수행한 결과로 정렬하거나 검색하는 작업은 B-Tree 인덱스의 장점을 이용할 수 없다.

8.3.3 B-Tree 인덱스 사용에 영향을 미치는 요소

8.3.3.1 인덱스 키 값의 크기

MySQL의 B-Tree에서 자식 노드의 개수는 인덱스의 페이지 크기와 키 값의 크기에 따라 결정된다. innodb_page_size 시스템 변수를 이용해 4~64KB 사이의 값을 선택할 수 있는데, 기본값은 16KB이다. 인덱스 키가 16바이트이고, 자식 노드 주소가 12바이트라고 가정했을 때, 하나의 인덱스 페이지에는 16 x 1024/(16+12) = 585개를 저장할 수 있다. 만약 인덱스 키 값이 두 배인 32바이트로 늘어났을 때는 16 x 1024/(32+12) = 372개 저장할 수 있다. SELECT 쿼리가 레코드 500개를 읽어와야 한다면 후자의 경우에는 최소 두 번 이상 디스크에서 읽어와야 한다.

또한, 인덱스 키 값의 길이가 길어진다는 것은 전체적인 인덱스의 크기가 커진다는것을 의미하는데, 인덱스를 캐싱하는 InnoDB 버퍼 풀이나 MyISAM의 키 캐시 영역은 크기가 제한적이기 때문에 인덱스의 크기가 커지면 그만큼 메모리에 캐시해 둘 수 있는 레코드 수가 줄어들게 된다.

8.3.3.2 B-Tree의 깊이

트리의 깊이는 상당히 중요하지만 직접 제어할 수는 없다. B-Tree의 깊이는 값을 검색할 때 몇 번이나 랜덤하게 디스크를 읽어야 하는지와 직결된다. 인덱스 키 값의 크기가 커지면 하나의 인덱스 페이지가 담을 수 있는 인덱스 키 값의 개수가 적어지고, 때문에 같은 레코드 건수라 하더라도 B-Tree의 깊이가 깊어져서 디스크 읽기가 더 많이 필요하게 된다.

8.3.3.3 선택도(기수성, Cardinality)

선택도 또는 기수성은 모든 인덱스 키 값 가운데 유니크한 값의 개수를 의미한다. 중복된 키 값이 많아질수록 선택도는 낮아진다. 인덱스는 선택도가 높을수록 검색 대상이 줄어들기 때문에 빠르게 처리된다. 참고로 선택도가 좋지 않아도 정렬이나 그루핑과 같은 작업을 위해 인덱스를 만드는 것이 훨씬 나은 경우도 있다.

8.3.3.4 읽어야 하는 레코드의 건수

일반적인 DBMS의 옵티마이저는 인덱스를 통해 레코드 한 건을 읽는 것이 테이블에서 직접 읽는 것보다 4~5배 정도 비용이 더 많이 드는 작업으로 예측한다. 따라서 인덱스를 통해 읽어야 할 레코드의 개수가 전체 테이블 레코드의 20~25%를 넘으면 인덱스를 이용하지 않는 것이 효율적이다.

8.3.4 B-Tree 인덱스를 통한 데이터 읽기

8.3.4.1 인덱스 레인지 스캔

가장 대표적인 접근 방식으로, 인덱스 풀 스캔이나 루즈 인덱스 스캔보다 빠르다. 인덱스 레인지 스캔은 검색해야 할 인덱스의 범위가 결정됐을 때 사용하는 방식이다.

SELECT * FROM employees WHERE first_name BETWEEN 'Ebbe' AND 'Gad';


위 그림처럼 시작 위치를 찾으면 그때부터는 리프 노드의 레코드만 순서대로 읽으면 된다. 최종적으로 스캔을 멈춰야 할 지점까지 다다르면 지금까지 읽은 레코드를 사용자에게 반환하고 쿼리를 끝낸다. 인덱스 레인지 스캔은 크게 다음과 같이 3단계로 나뉜다.

  1. 인덱스 탐색: 인덱스에서 조건을 만족하는 값이 저장된 위치를 찾는다.
  2. 인덱스 스캔: 1번에서 탐색된 위치부터 필요한 만큼 인덱스를 차례대로 읽는다.
  3. 2번에서 읽어 들인 인덱스 키와 레코드 주소를 이용해 레코드가 저장된 페이지를 가져오고, 최종 레코드를 읽어온다.

커버링 인덱스로 처리되는 쿼리는 디스크의 레코드를 읽지 않아도 되기 때문에 랜덤 I/O가 상당히 줄어들고 그만큼 성능이 빨라진다.

8.3.4.2 인덱스 풀 스캔

인덱스 레인지 스캔과 마찬가지로 인덱스를 사용하지만, 인덱스의 처음부터 끝까지 모두 읽는 방식이다. 대표적으로 쿼리의 조건절에 사용된 컬럼이 인덱스의 첫 번째 칼럼이 아닌 경우 인덱스 풀 스캔 방식이 사용된다. 인덱스가 (A, B, C)로 만들어져 있는데 쿼리의 조건절에서는 B나 C 칼럼으로 검색하는 경우이다. 보통 인덱스의 크기가 테이블의 크기보다 작기 때문에 테이블 풀 스캔보다는 효율적이다. 쿼리가 인덱스에 명시된 칼럼만으로 조건을 처리할 수 있는 경우 주로 사용된다.

8.3.4.3 루즈(Loose) 인덱스 스캔

이름 그대로 느슨하게, 또는 듬성듬성하게 인덱스를 읽는 방식이다. 앞의 두 방식은 반대로 타이트 인덱스 스캔으로 분류한다. 인덱스 레인지 스캔과 비슷하게 동작하지만 중간에 필요하지 않은 인덱스 키 값은 무시하고 넘어간다. 일반적으로 GROUP BY 또는 집합 함수 가운데 MAX(), MIN() 함수 최적화에 사용된다.

SELECT dept_no, MIN(emp_no)
FROM dept_emp
WHERE dept_no BETWEEN 'd002' AND 'd004'
GROUP BY dept_no;

8.3.4.4 인덱스 스킵 스캔

아래의 예시와 같이 멀티 키로 인덱스가 구성되었을 때, 첫번째 칼럼이 아니라 두번째 칼럼으로 검색하는 경우 인덱스 스킵 스캔을 활용할 수 있다.

// 인덱스 생성
ALTER TABLE employees
ADD INDEX ix_gender_birthdate (gender, birth_date);

// 인덱스를 사용하지 못하는 쿼리
// 인덱스를 사용하려면 gender 칼럼에 대한 비교 조건이 필수다.
SELECT * FROM employees WHERE birth_date>='1965-02-01';

// 인덱스를 사용할 수 있는 쿼리
SELECT * FROM employees WHERE gender='M' AND birth_date>='1965-02-01';

위 경우, gender 칼럼이 enum 값으로 'M', 'F' 값을 가지므로 옵티마이저가 각각에 대해 쿼리를 수행한다.

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';


gender 칼럼에서 유니크한 값을 모두 조회해서 주어진 쿼리에 gender 칼럼의 조건을 추가해서 쿼리를 다시 실행하는 형태로 처리한다.성별처럼 선두 칼럼의 고유 값의 개수가 적고 후행 칼럼의 고유 값이 많을 때 효과적이다. 첫번째 칼럼을 스킵한다고 해서 인덱스 스킵 스캔이라고 하는데 이 기능은 결국 여러 인덱스 레인지 스캔의 합을 붙였다고 볼 수 있다.

다만 인덱스 스킵 스캔은 아직 다음과 같은 단점이 있다.

  1. WHERE 절에 조건이 없는 인덱스의 선행 칼럼의 유니크한 값의 개수가 적어야 함: 유니크한 개수가 매우 많다면 옵티마이저가 인덱스에서 스캔해야 할 시작 지점을 검색하는 작업이 많이 필요해진다.
  2. 쿼리가 인덱스에 존재하는 칼럼만으로 처리 가능해야 함(커버링 인덱스)

8.3.5 다중 칼럼(Multi-column) 인덱스

실제 서비스용 데이터베이스에서는 두 개 이상의 칼럼을 포함하는 인덱스가 많이 사용된다. 이러한 인덱스를 다중 칼럼 인덱스(복합 칼럼 인덱스)라고 한다. 아래 그림과 같이 인덱스의 두 번째 칼럼은 첫 번째 칼럼에 의존해서 정렬된다. 따라서 다중 칼럼 인덱스의 칼럼 순서를 정하는 것은 매우 중요하다.

8.3.6 B-Tree 인덱스의 정렬 및 스캔 방향

인덱스 키값은 항상 오름차순으로만 정렬되지만 사실 그 인덱스를 거꾸로 끝에서부터 읽으면 내림차순으로 정렬된 인덱스로도 사용될 수 있다. 인덱스를 어느 방향으로 읽을지는 쿼리에 따라 옵티마이저가 실시간으로 만들어 내는 실행 계획에 따라 결정된다.

8.3.6.1 인덱스의 정렬

8.0버전부터는 다중 칼럼 인덱스에서 칼럼 단위로 정렬 순서를 혼합할 수 있다.

CREATE INDEX ix_teamname_userscore ON employees (team_name ASC, USER_score DESC);
  • 인덱스 스캔 방향
    인덱스는 항상 오름차순으로만 정렬돼 있지만 인덱스를 최소 값부터 읽으면 오름차순으로 값을 가져 올 수 있고, 최댓값부터 거꾸로 읽으면 내림차순으로 값을 가져올 수 있다는 것을 MySQL 옵티마이저는 이미 알고 있다. 그래서 위의 쿼리는 인덱스를 역순으로 접근해 첫 번째 레코드만 읽으면 된다.
SELECT * 
FROM employees 
ORDER BY first_name DESC 
LIMIT 1;

SELECT * FROM employees WHERE first_name>='Anneke'
	ORDER BY first_name ASC LIMIT 4;

SELECT  * FROM employees 
	ORDER BY first_name DESC LIMIT 5;

첫 번째 쿼리는 first_name 칼럼에 정의된 인덱스를 이용해 "Anneke"라는 레코드를 찾은 후 오름차순으로 해당 인덱스를 읽으면서 4개의 레코드만 가져오면 아무런 비용을 들이지 않고도 원하는 정렬 효과를 얻을 수 있다. 두 번째 쿼리는 이와 반대로 employees 테이블의 first_name 칼럼에 정의된 인덱스를 역순으로 읽으면서 처음 다섯 개의 레코드만 가져오면 되는 것이다. 쿼리의 ORDER BY 처리나 MIN() 또는 MAX() 함수 드의 최적화가 필요한 경우, MySQL 옵티마이저는 인덱스의 읽기 방향을 전환해서 사용하도록 실행 계획을 만들어 낸다.

  • 내림차순 인덱스
  • 오름차순 인덱스(Ascending Index): 작은 값의 인덱스 키가 B-Tree의 왼쪽으로 정렬된 인덱스
  • 내림차순 인덱스(Descending Index): 큰 값의 인덱스 키가 B-Tree의 왼쪽으로 정렬된 인덱스
  • 인덱스 정순 스캔(Foward Index Scan): 인덱스 키의 크고 작음에 관계없이 인덱스 리프 노드의 왼쪽 페이지부터 오른쪽으로 스캔
  • 인덱스 역순 스캔(Backward Index Scan): 인덱스 키의 크고 작음에 관계없이 인덱스 리프 노드의 오른쪽 페이지부터 왼쪽으로 스캔

내부적으로는 InnoDB에서 인덱스 역순 스캔이 인덱스 정순 스캔에 비해 느릴 수 밖에 없다.

  1. 페이지 잠금이 인덱스 정순 스캔에 적합한 구조
  2. 페이지 내에서 인덱스 레코드가 단방향으로만 연결된 구조

8.3.7 B-Tree 인덱스의 가용성과 효율성

쿼리의 WHERE 조건이나 GROUP BY, ORDER BY 절이 어떤 경우에 인덱스를 사용할 수 있고 어떤 방식으로 사용할 수 있는지 알아보자.

8.3.7.1 비교 조건의 종류와 효율성

다중 칼럼 인덱스에서 각 칼럼의 순서와 그 칼럼에 사용된 조건이 동등 비교=인지, 아니면 크거나 작은>, < 범위 조건인지에 따라 각 인덱스 칼럼의 활용 형태와 효율이 달라진다.

SELECT * FROM dept_emp
WHERE dept_no='d002' AND emp_no >= 10114;

위 쿼리에 대해 다음과 같이 두 가지 경우의 인덱스가 있을 때의 차이를 보자.
케이스 A: INDEX(dept_no, emp_no)
케이스 B: INDEX(emp_no, dept_no)

다중 칼럼 인덱스에서는 두 번째 칼럼은 첫 번째 칼럼에 대해서 정렬이 되기 때문에 케이스 A에서는 두 번째 조건의 칼럼 emp_no가 범위를 줄이는데 도움이 되지만, 케이스 B에서의 dept_no는 비교 작업의 범위를 좁히는데 도움이 되지 못한다. 작업 범위를 결정하는 조건은 많을수록 쿼리 처리 성능을 높이지만 단순 체크 조건은 쿼리 처리 성능을 높이지 못한다.

8.3.7.2 인덱스의 가용성

B-Tree 인덱스의 특징은 왼쪽 값에 기준해서(Left-most) 오른쪽 값이 정렬되어 있다는 것이다. 여기서 왼쪽은 다중 칼럼 인덱스의 칼럼에 대해서도 함께 적용된다.
가용성: 서버와 네트워크, 프로그램 등의 정보 시스템이 정상적으로 사용 가능한 정도

SELECT * FROM employees WHERE first_name LIKE '%mer';
SELECT * FROM dept_emp WHERE emp_no>=10144;


이 쿼리는 왼쪽 부분이 고정되지 않았기 때문에 인덱스 레인지 스캔 방식을 쓸 수 없다.
이 쿼리 역시 인덱스가 dept_no 칼럼에 대해 먼저 정렬이 되어 있기 때문에 인덱스를 효율적으로 사용할 수 없다.

8.3.7.3 가용성과 효율성 판단

기본적으로 B-TREE 인덱스의 특성상 다음 조건에서는 사용할 수 없다. 여기서 사용할 수 없다는 것은 작업 범위 결정 조건으로 사용할 수 없다는 것을 의마하며, 경우에 따라서는 체크 조건으로 인덱스를 사용할 수는 있다.

  1. NOT-EQUAL("<>", "NOT IN", "NOT BETWEEN", "IS NOT NULL")
  2. LIKE '%??' (뒷부분만 일치)
  3. 스토어드 함수나 다른 연산자로 인덱스 칼럼이 변형된 후 비교된 경우
    • where DAYOFMONTH(column) = 1
  4. NOT-DETERMINISTIC 속성의 스토어드 함수가 비교 조건에 사용된 경우\
    • where column = deterministic function()
  5. 데이터 타입이 서로 다른 비교
  6. 문자열 데이터 타입의 콜레이션이 다른 경우
    • where utf8_bin_char_column = euckr_bin_bin_char_column
  • 작업 범위 결정 조건으로 인덱스를 사용하지 못하는 경우

    • column_1 칼럼에 대한 조건이 없는 경우
    • column_1 칼럼의 비교 조건이 위의 인덱스 사용 불가 조건 중 하나인 경우
  • 작업의 범위 결정 조건으로 인덱스를 사용하는경우(i는 2보다 크고 n보다 작은 임의의 값을 의미)

    • column1 ~ column(i-1) 칼럼까지 Equal 형태("=" 또는 "IN")로 비교
    • column_i 칼럼에 대해 다음연산자 중 하나로 비교
      • Equal("=" 또는 "IN")
      • 크다 작다 형태(">" 또는 "<")
      • LIKE로 좌측 일치 패턴(LIKE '승환%')

8.4 R-Tree 인덱스

MySQL의 공간 인덱스(Spatial Index)는 R-Tree 인덱스 알고리즘을 이용해 2차원의 데이터를 인덱싱하고 검색하는 인덱스다. R-Tree의 내부 메커니즘은 B-Tree와 흡사한데, B-Tree는 인덱스를 구성하는 칼럼의 값이 1차원의 스칼라 값인 반면에 R-Tree 인덱스는 2차원의 공간 개념 값이라는 점이 다르다.

MySQL의 공간 확장(Spatial Extension)을 이용하면 위치 기반의 서비스를 쉽게 구현할 수 있다. 다음과 같이 크게 세 기능이 있다.

  1. 공간 데이터를 저장할 수 있는 데이터 타입
  2. 공간 데이터의 검색을 위한 공간 인덱스(R-Tree 알고리즘)
  3. 공간 데이터의 연산 함수(거리 또는 포함 관계의 처리)

8.4.1 구조 및 특성

대표적인 공간 정보를 저장하는 데이터 타입은 다음과 같다. GEOMETRY 데이터 타입에는 POINT, LINE, POLYGON 데이터 타입을 모두 저장할 수 있다.
MBR(Minimum Bounding Rectangle)이란 해당 도형을 감싸는 최소 크기의 사각형을 의미하는데, 이 사각형들의 포함 관계를 B-Tree 형태로 구현한 인덱스가 R(Rectangle)-Tree 인덱스다.

최상위 MBR은 루트 노드에 저장되는 정보, 차상위 MBR은 브렌치 노드에 저장되는 정보이고 최하위 레벨은 리프 노드에 저장되는 정보이다.

8.4.2 R-Tree 인덱스의 용도

R-Tree 인덱스는 일반적으로 GPS 기준 위/경도 좌표 저장에 사용된다. 뿐만 아니라 CAD/CAM 나 회로 디자인 등과 같이 좌표 시스템에 기반을 둔 정보에 대해서 모두 적용할 수 있다.
도형의 MBR의 포함 관계를 이용해 만든 인덱스이기 때문에 ST_Contains()나 ST_Within() 등과 같은 포함 관계를 비교하는 함수로 검색하는 경우에만 인덱스를 이용할 수 있다.

// ST_Contains() 또는 ST_Within()을 이용해 "사각 상자"에 포함된 좌표 Px만 검색
SELECT * FROM tb_location
WHERE ST_Contains(사각 상자, px);

SELECT * FROM tb_location
WHERE ST_Within(px, 사각 상자);

// P6도 제거해야 하는 경우
SELECT * FROM tb_loaction
WHERE ST_Contians(사각 상자, px)
	AND ST_Distance_Sphere(p, px) <= 5*1000/*5km*/;
profile
안녕하세요!!

0개의 댓글