쿼리의 성능에 있어 인덱스는 빠질 수 없는 부분이다.
MongoDB 에서 사용할 수 있는 인덱스와 종류에 대해 살펴본다.
또한 랜덤 I/O 와 순차 I/O 에 대해 살펴본다.
데이터베이스와 관련된 성능의 튜닝은 디스크 I/O 를 얼마나 줄이냐가 관건인 것들이 많이 있다.
디스크 읽기 방식
디스크 저장 매체
컴퓨터 장치의 성능을 보면 기계식 장치인 디스크가 가장 취약한 성능을 보였다.
요즘은 이런 기계식에서 전자식으로 교체되고 있고 대표적으로 SSD 가 있다.
SSD 는 D-RAM 과 달리 전원 공급에 관계없이 한번 기록한 내용을 영구적으로 보존하는 플래시 메모리를 내장하고 있고,
D-RAM 보다는 떨어지지만 기계식 HDD 와 비교하면 월등하게 빠르다.
SSD 의 가장 큰 장점은 랜덤 I/O 에 매우 적합하다는 것이다.
순차 I/O 는 DB 에서 그다지 중요한 요소는 아니며 HDD 도 순차 I/O 성능은 크게 문제되지 않는다.
랜덤 I/O 와 순차 I/O
순차 I/O 는 3개의 페이지를 디스크에 기록하기 위해 시스템 콜을 1번한다.
디스크에 기록해야 할 위치를 찾기 위해 디스크의 헤더를 1번만 움직인 것이다.
랜덤 I/O 는 3개의 페이지를 디스크에 기록하기 위해 시스템 콜을 3번한다.
즉 디스크의 헤더를 3번 움직인 것이다.
디스크의 부하는 얼마나 많은 데이터를 한번에 기록하는지 보다는, 얼마나 자주 디스크에 기록을 요청하는지에 의존적이어서
여러 번 쓰기나 읽기를 요청하는 작업이 부하가 훨씬 크다.
그래서 쿼리를 튜닝 시 랜덤 I/O 횟수를 얼마나 줄이는지에 따라 달려있다.
인덱스란
데이터의 저장 성능을 희생해서 상대적으로 읽기 속도를 향상시킨다.
MongoDB 는 Key 가 Index 와 같은 의미이므로 함께 사용된다.
인덱스를 역할별로 구분하면 프라이머리 와 세컨드리 키로 구분된다.
데이터 저장 방식은 B-Tree 와 Hash 인덱스가 대표적이다.
MongoDB 에서는 전문 인덱스나 공간 인덱스등의 알고리즘도 지원한다.
데이터의 중복을 허용하는지 여부로 분류하면 유니크와 유니크하지 않은 인덱스로 구분이 가능하다.
MongoDB 인덱스의 개요
클러스터링 인덱스
클러스터링 인덱스는 책에 정리되어 있지 않지만, 현재는 제공되고 있다.
간단히 요약하면 클러스터 인덱스 키 값을 기준으로 문서를 정렬하여 저장한다.
클러스터형 인덱스 키는 _id 이고, 문서는 하나의 순서로만 저장가능하므로 컬렉셔 클러스터 인덱스는 하나만 있을 수 있다.
레코드를 저장하는 시점에 인덱스 키 값 순서대로 데이터를 저장하기 때문에 INSERT 가 느리게 처리되지만,
클러스터링 키 값을 기준으로 범위 검색을 수행하는 경우 별도의 랜덤 액세스 없이 레코드를 읽기 때문에 매우 빠르게 범위스캔이 가능하다.
https://www.mongodb.com/ko-kr/docs/manual/core/clustered-collections/
인덱스 내부
MongoDB 엔진과 스토리지 엔진은 각각의 레이러로 구분되어 있고 플러그인 형태로 끼워넣을 수 있어 다양한 스토리지 엔진을 사용할 수 있다.
그러나 이런 스토리지 엔진들이 컬렉션과 인덱스를 구현하는 방법에는 조금씩 차이가 있다.
브랜치 노드에 있는 인덱스 키 엔트리는 키와 값의 쌍을 가지며, 키는 사용자가 인덱스를 생성할 때 선택한 필드이며 값은 자식노드의 주소를 가리킨다.
리프 노드에 있는 인덱스 키 엔트리도 키와 값의 쌍을 가지며, 키는 사용자가 인덱스를 생성할 때 선택한 필드이며 값은 내부적으로 키 값과 연결된 도큐먼트의 저장 주소를 의미한다.
- 브랜치 : 인덱스 키 -> 자식 노드 주소
- 리프 : 인덱스 키 -> 레코드 주소
MMAPv1 스토리지 엔진의 Record-Id
인덱스 키의 Record-Id 부분에 실제 도큐먼트가 저장된 물리적 주소를 저장한다.
MMAPv1 스토리지 엔진에서 도큐먼트의 이동을 경계하는 이유는 도큐먼트가 옮겨질때마다 도큐먼트의 물리 주소를 가지고 있는 인덱스의 엔트리를 찾아서 모두 변경해줘야 하기 때문이다.
이는 많은 디스크 랜덤 액세스를 유발하므로 상당히 느리게 처리된다.
WiredTiger 스토리지 엔진의 Record-Id
MMAPv1 스토리지 엔진과 달리 인덱스 키 엔트리에 논리 주소를 사용한다.
정확히 논리주소라기보다는 도큐먼트마다 고유의 식별자를 할당하여 Record-Id 로 부여한다.
도큐먼트의 고유 식별자는 자동 증가값인 Auto Increment 로 1씩 자동증가하는 시퀀스를 사용한다.
Record-Id 도 64비트 정수 타입을 사용하고 컬렉션 단위로 별도의 자동증가값을 사용한다.
그리고 문서가 데이터파일 내에서 위치가 이동되더라도 처음 할당된 논리적인 주소값은 변하지 않고 계속 유지된다.
그럼 이 자동증가값으로 어떻게 실제 데이터 파일의 도큐먼트를 찾는지는
WiredTiger 스토리지 엔진은 내부적으로 Record-Id 값을 인덱스로 가지는 내부 인덱스를 하나 더 가진다.
이 내부 인덱스는 Record-Id 값을 키로하는 클러스터링 인덱스를 가진다.
따라서 WiredTiger 에서 검색 시 두 번의 인덱스 검색을 수행해야 최종 결과를 얻을 수 있어 조회성능은 느려지나,
변경은 훨씬 유연하게 처리할 수 있게되었다.
로컬 인덱스
다양한 형태의 세컨드리 인덱스를 지원한다.
MongoDB 의 인덱스는 로컬인덱스로 관리되므로 각 샤드가 저장하고 있는 도큐먼트에 대한 인덱스만 가진다.
그래서 프라이머리 인덱스나 유니크 인덱스는 샤드 키를 반드시 포함해야 하거나 응용 프로그램 수준에서 유니크함을 보장해야 한다.
MongoDB 의 인덱스도 다른 DBMS 와 동일하게 쿼리 성능을 높이는 반면, 생성 및 변경처리 성능을 저하시킨다.
각 샤드의 데이터를 균등하게 배치하기 위해 밸런서가 백그라운드로 샤드 간 데이터를 자동으로 분산한다.
이 때도 데이터를 생성 및 삭제하는 작업을 수행하는데, 인덱스가 많은 컬렉션은 그만큼 데이터 밸런싱 작업을 지연시키고 부하를 일으키는 원인이 된다.
인덱스 키 엔트리 자료 구조
도큐먼트는 MongoDB 내부적으로 BSON 이라는 형태의 JSON 에서 변형된 포맷으로 저장된다.
MongoDB 도큐먼트는 키 값 쌍으로 된 JSON 포맷을 사용하므로 데이터 파일에 필드명과 필드 값이 같이 저장된다.
그래서 MongoDB 데이터는 스키마를 가지는 RDBMS 보다 디스크에 저장되는 데이터 용량이 더 크다.
이렇게 키 값 쌍으로 구성된 구성된 도큐먼트는 MongoDB 의 컬렉션에만 해당되며
인덱스의 내부 저장 구조는 도큐먼트나 BSON 자료 구조를 사용하지 않는다.
일반적으로 MongoDB 를 스키마프리 데이터베이스로 생각하나, 인덱스에서는 맞지 않는 이야기이다.
인덱스는 내부적으로 별도의 스키마를 가지고 있고 각 인덱스가 어떤 종류의 인덱스이고 이 인덱스를 구성하는 필드가 어떤것인지에 대한 메타정보를 가지지 않는다.
스키마를 가지고 있기에 인덱스의 각 키 엔트리에 필드명을 굳이 저장할 이유가 없다.
그래서 B-Tree 인덱스에서는 필드의 명칭이 들어가 있지 않다.
B-Tree 인덱스
B-Tree 는 데이터베이스의 인덱싱 알고리즘으로 가장 일반적이고 오래되면서 범용적인 목적을 만족시키는 알고리즘이다.
B-Tree 에 변형된 형태가 있지만 조금씩 차이가 있을 뿐이다.
B-Tree 구조 시 이진트리의 B 라고 생각할 수 있지만, 바이너리 트리와 일반 DBMS 에서 사용되는 B-Tree 는 많은 차이가 있다.
많은경우 B-Tree 를 2개의 자식 노드만 가진다고 생각하는 경우가 많지만 단순히 표현해서 이런거고
DBMS 에서 사용하는 B-Tree 가 항상 자식노드를 2개만 가지는 바이너리 트리임을 의미하는 것은 아니다.
구조 및 특성
인덱스 리프 노드의 각 키 값은 테이블의 데이터 레코드를 찾아가기 위한 물리적인 주소 값을 가지고 있다.
인덱스의 키 값은 모두 정렬되어 있지만, 테이블의 데이터 레코드는 기본적으로 정렬되어 있지 않고 INSERT 된 순서대로 저장된다.
레코드 주소는 도큐먼트의 물리적인 위치일수도 있고 논리적인 시퀀스 값일수도 있다.
이는 스토리지 엔진에 따라 조금씩 의미가 다를 수 있다.
MongoDB 컬렉션의 인덱스는 항상 인덱스 필드의 값과 주소 값(RecordID) 의 조합이 인덱스 레코드로 구성된다.
MongoDB 는 컬럼 명세를 가지지 않기 때문에 NOSQL 로 분류되나, 인덱스는 반드시 컬럼의 명세를 가진다.
인덱스 데이터에는 필드 이름을 관리하지 않고 인덱스의 메타 정보에 필드 이름이 관리된다.
다만 필드 이름이 포함되는 경우가 있는데, 필드에 서브 도큐먼트를 가지는 경우이다.
{
name: "mozi",
Phone: {
brand: "SKT",
number: "010-1234-5678"
}
}
createIndex({name:1})
createIndex({Phone:1})
name 은 인덱스에 필드가 들어가지 않지만,
Phone 은 서브 도큐먼트가 있고 인덱스에는 서브 도큐먼트의 필드 brand , number 가 모두 들어간다.
B-Tree 인덱스 키 추가 및 삭제
인덱스 키 추가
키 값이 존재하지 않으면 리프 노드에 인덱스 값을 추가하고 그 하위에 데이터 레코드가 저장된 위치를 저장한다.
노드가 꽉 차게되면 노드를 분리해야 하는데 이 경우에는 브랜치 노드의 변경이 필요할 수도 있다.
인덱스 키 삭제
B-Tree 의 키 값을 삭제할 때는 간단한데, 키 값을 찾아서 삭제 마크만 하면 작업이 완료된다.
마킹 작업 또한 디스크 쓰기가 발생하지만, 내부적으로 캐시를 가지고 있기 때문에 변경된 데이터를 디스크에 기록하는 작업은 사용자 데이터 변경 요청과는 비동기로 처리된다.
인덱스 키 변경
키 값을 변경하는 경우는 먼저 키 값을 삭제한 다음 새로운 키 값을 추가하는 형태로 처리된다.
키 값의 삭제와 추가 작업은 위의 작업과 동일하다.
인덱스 키 검색
인덱스를 검색하는 작업은 B-Tree 의 루트노드부터 시작해 브랜치노드를 거쳐 최종 리프노드까지 비교하는 과정을 통해 이동하는데, 이 과정을 트리검색이라 한다.
인덱스를 이용한 검색은 100% 일치 또는 값의 앞 부분이 일치할 때만 사용할 수 있고
부등호 비교에는 B-Tree 인덱스 검색 기능을 이용할 수 없다.
인덱스 컬럼 값이 변형이 가해진 후 비교검색을 하게되면 B-Tree 기능을 사용할 수 없는데,
변형된 값은 인덱스에 존재하지 않기 때문에 B-Tree 특성을 이요할 수 없게된다.
B-Tree 인덱스 사용에 영향을 미치는 요소
인덱스를 구성하는 필드의 사이즈와 도큐먼트 건수, 유니크한 값 개수등에 따라 검색이나 변경 작업의 성능이 영향을 미친다.
인덱스 키 값의 사이즈
디스크에 데이터를 저장하는 가장 기본 단위를 페이지 또는 블록이라 표현하며, 읽기 및 쓰기의 최소 작업 단위이다.
페이지는 각 스토리지 엔진이 데이터를 관리하는 기본 단위로, 인덱스도 페이지 단위로 관리된다.
루트 브랜치 리프 노드를 구분한 기준 또한 페이지 단위로,
자식 노드를 몇개 가지는지는 인덱스 페이지 크기와 키 값의 사이즈에 따라 결정된다.
예를들어 WiredTiger 스토리지 엔진에서 페이지 크기를 16KB 로 사용하는 경우,
인덱스의 키가 16바이트이고 값을 12바이트라고 가정한다.
단순계산하면 16*1024 / (16+12) = 585 개를 저장할 수 있다.
그러면 최종적으로 자식노드를 585개 가질 수 있는 B-Tree 가 될 수 있다.
인덱스 키 값이 커지면 자식노드를 그만큼 적개 가지게 된다.
이러면 페이지를 더 많이 읽어야 하고 그만큼 느려진다는 것을 의미한다.
B-Tree 깊이 ( Depth )
인덱스 키 값의 사이즈가 커질수록 하나의 페이지가 담을 수 있는 개수가 작아지고,
그로인해 동일한 데이터 건수라 하더라도 B-Tree 의 깊이가 깊어져 디스크 읽기가 더 많이 발생한다.
이러한 이유로 인덱스 키 값의 사이즈는 가능하면 작게 만드는 것이 좋다.
실제로는 Depth 가 5 이상으로 깊어지는 경우는 거의 없어, Depth 로 인해 성능이 저하되는 현상은 많이 없다.
선택도
선택도는 모든 인덱스 키 값 중에서 유니크한 값의 개수를 의미한다.
인덱스 키 값 중에서 중복된 값이 많아지면 많아질수록 동시에 선택도가 떨어진다.
인덱스는 선택도가 높아야 좋고 그만큼 빠른 검색이된다.
읽어야 하는 레코드의 건수
인덱스를 통해 컬렉션의 도큐먼트를 읽는것은 상당히 고비용 작업이다.
인덱스를 이용한 읽기의 손익 분기점을 판단해야 하는데,
인덱스를 통한 도큐먼트 읽기 작업이 15~20 % 를 넘어서면 인덱스를 이용하지 않고 컬렉션 스캔으로 필요한 레코드만 가려내는 방식으로 처리한다.
읽어야 할게 많은 도큐먼트는 인덱스를 사용하도록 힌트를 추가해도 성능적으로 얻을 수 있는 이득이 없다.
B-Tree 인덱스를 통한 데이터 읽기
인덱스 레인지 스캔
인덱스 레인지 스캔은 인덱스 접근 방법 중에서 가장 대표적인 접근 방식이다.
인덱스 스캔을 시작할 위치를 어떻게 선택하고, 일치하는 건에 대해 데이터 레코드를 읽기 위해 어떤 작업이 필요한지 본다.
인덱스 레인지 스캔은 검색해야 할 인덱스의 범위가 결정된 경우에 사용할 수 있는 방식으로
검색하고자 하는 값의 수나 검색 결과 레코드의 건수와 관계없이 레인지 스캔이라고 표현한다.
원하는 시작점을 찾기 위해 루트노드부터 시작해서 브랜치 노드를 거쳐 최종적으로 리프 노드의 시작 지점을 찾는다.
리프 노드에서 시작해야 할 위치를 찾게 되면 그때부터 리프 노드 간의 링크를 이용해서 리프 노드만 스캔한다.
최종 스캔을 멈춰야 할 위치에서 사용자에게 결과를 반환하면 처리가 완료된다.
가장 중요한 것은 인덱스 자체가 정렬되어 있기 때문에 어떤 방식으로 인덱스를 스캔하든지 가져오는 레코드의 순서는 정순 또는 역순으로 정렬된 상태로 가져온다.
또 인덱스의 리프 노드에서 검색 조건과 일치하는 건들에 대해서 데이터 파일의 실제 데이터 레코드를 읽어오는 과정은 레코드 한 건 한건별로 랜덤 I/O 가 필요하다.
만약 3건의 레코드가 검색 조건과 일치한다고 가정하면 랜덤 I/O 는 3번이 필요하다.
그래서 인덱스를 통해 데이터 레코드를 읽는 작업은 비용이 많이 드는 작업이라고 판단한다.
인덱스 프리픽스 스캔
문자열 필드를 대상으로 일부만 일치하는 패턴을 검색하고자 할 때 사용한다.
문자열의 좌측 일치 검색을 수행하는 정규 표현식이 인덱스를 활용하려면 3가지 조건을 맞춰야 한다.
- 반드시 문자열의 처음부터 일치하도록 되어야 한다.
- 검색 문자열이 시작표시 이외의 정규 표현식을 포함하지 않아야 한다.
- 문자열의 마지막을 표현하는 $ 표시가 없어야 한다.
이 조건들은 SQL 에서 자주 사용되는 좌측 일치 LIKE 연산과 같은 조건으로
일반적인 레인지 스캔과 동일한 방식으로 작동한다.
커버링 인덱스
인덱스만으로 쿼리를 처리할 수 있는 경우에는 도큐먼트가 저장된 컬렉션 데이터 파일을 읽지 않고 쿼리를 처리한다.
이렇게 컬렉션의 데이터 파일은 전혀 참조하지 않고 인덱스만 읽어서 쿼리가 처리되는 최적화를 커버링 인덱스라고 하고,
그 쿼리를 인덱스 커버 쿼리라고 한다.
인덱스 인터섹션
일반적으로 하나의 쿼리는 하나의 인덱스를 이용해서 처리된다. * 여러 컬렉션을 사용하는 경우 컬렉션별 인덱스 사용
그런데 인덱스 인터섹션 최적화를 사용하는 경우 2개의 인덱스를 사용하며 MongoDB 서버는 3.0 버전부터 인터섹션 최적화를 지원한다.
* OR 조건이 사용된 경우도 여러 개 인덱스를 이용하는 경우가 있지만 이는 INTERSECT 연산이 아닌 각각의 하위 조건이 각각의 인덱스를 사용하여 결과를 나타내고 UNION 으로 병합하는 과정이 실행된다.
하지만 인덱스 인터섹션 최적화는 웬만해서는 경험하기 어려운 최적화로, 그 이유는 효율적인 경우가 별로 없기 때문이다.
만약 인터섹션이 사용되었는지 확인하고 싶다면 AND_SORTED 와 AND_HASHED 가 실행계획에 있는지 확인하면 된다.
이 둘의 가장 큰 차이는 각 인덱스 검색의 결과가 RecordId 를 기준으로 정렬되어 있는지다.
인덱스 인터섹션이 사용되는 경우는 어떤 인덱스로도 최적화하기가 어렵다고 판단되는 경우이다.
대부분의 경우 쿼리가 가장 효율적으로 처리될 수 있는 방법은 여러 조건이 하나의 인덱스를 이용해서 검색 범위를 좁힐 수 있는 경우이다.
인덱스 풀 스캔
인덱스 리프 노드의 제일 앞 또는 제일 뒤로 이동한 다음 인덱스의 리프노드를 연결하는 링크드 리스트를 따라서 처음부터 끝까지 스캔하는 방식을 인덱스 풀 스캔이라고 한다.
인덱스 레인지 스캔보다는 빠르지 않지만 컬렉션 풀 스캔을 하는것보다는 효율적이다.
이는 인덱스에 포함된 필드만으로 쿼리를 처리할 수 있을 때에는 컬렉션의 도큐먼트를 읽을 필요가 없어, 디스크 I/O 를 작게 유발하기 때문이다.
컴파운드 인덱스 (compound)
위의 인덱스는 모두 필드를 1개만 가진 인덱스이다.
그러나 실제로 2개 이상의 필드를 가지는 인덱스가 더 일반적이다.
인덱스의 N 번째 컬럼은 N-1번째 컬럼이 같은 레코드 내에서 다시 정렬된다.
즉 N 번째 컬럼이 아무리 정렬 순서가 빠르다 하더라도 N-1 컬럼의 정렬순서가 늦다면 인덱스 뒤쪽에 위치하게 된다.
이러한 이유로 인덱스 내에서 각 필드의 위치가 상당히 중요하다.
복합 필드 인덱스
하나의 필드로만 구성된 경우를 단일 필드 인덱스라고 하며
여러 필드가 복합적으로 연결된 인덱스를 컴파운드 인덱스라고 한다.
해시 인덱스는 단일필드로만 생성할 수 있으며
나머지 인덱스 알고리즘은 대부분 여러 필드를 복합적으로 묶어서 생성할 수 있다.
또한 여러 필드를 이용해서 컴파운드 인덱스를 생성하는 경우 인덱스를 구성하는 각 필드가 서로 달느 정렬 방식을 가질수도 있다.
단일 필드와 복합 필드의 기준
서브 도큐먼트를 가지는 도큐먼트 하나를 인덱스로 구성하면 단일 필드로 취급된다.
복합 필드 인덱스는 도큐먼트의 1레벨 필드 뿐 아니라 서브 도큐먼트의 필드도 포함할 수 있다.
또 서브 도큐먼트 각 필드의 조합으로 컴파운드 이넫그슬ㄹ 생성할 수도 있다.
단일 필드 인덱스는 해당필드에 저장되는 값이 어떤 값이든간에 BSON 으로 전환한 다음, 하나의 바이트 배열 값으로 판단한다.
그래서 필드의 서부 도큐먼트에서 필드의 순서가 변경된다면 다른 바이트 배열이 되기 때문에 다른 값으로 인식한다.
즉, 서브 도큐먼트의 순서를 다르게 저장하면 서로 다른 값으로 인식하므로 동일 검색 조건의 결과 값으로 조회할 수 없다.
만약 두 개의 필드 값이 각각 인덱스 키 엔트리로 참여하므로 검색 조건에 주어지는 필드의 순서와 관계없이 같은 조건으로 두 도큐먼트 모두 검색할 수 있다.
또 다른 큰 차이는 서브 도큐먼트를 가지는 도큐먼트가 필드 인덱스인경우, 어떤 서브 도큐먼트가 저장되더라도 그 값을 모두 BSON 으로 변환한 다음 전체 BSON 을 인덱스 키 엔트리로 사용하지만,
서브 도큐먼트를 인덱스로 하는경우 다른 서브 도큐먼트에 관계없이 그 중에서 인덱스로 구성되는 서브 필드 조합으로만 컴파운드 인덱스를 생성한다.
복합 인덱스의 장점
일반적으로 RDBMS 의 컴파운드 인덱스는 단일 값을 가지는 컬럼을 결합해서 B-Tree 인덱스를 생성한다.
하지만 MongoDB 인덱스는 여러 타입의 인덱스를 혼합해서 결합할 수도 있다.
B-Tree 인덱스와 공간 인덱스를 결합해서 하나의 인덱스를 생성할 수도 있고,
B-Tree 와 전문 인덱스를 결합해서 하나의 인덱스를 생성할 수도 있다.
B-Tree 인덱스의 정렬 및 스캔 방향
인덱스의 정렬
인덱스에서 각 필드의 정렬을 오름차순 또는 내림차순으로 결정할 수 있는 것은 B-Tree 인덱스만 가능하다.
인덱스를 생성할 때 인덱스 대상 필드를 1 또는 -1 로 설정한다.
1은 오름차순이며 -1 은 내림차순이다.
단일필드 인덱스인경우 -1, 1 은 인덱스를 어떻게 정렬하여 저장하는지 이며,
쿼리가 어떤 정렬을 가지던 옵티마이저는 뒤에서 읽거나 앞에서 읽도록 적절하게 판단한다.
다만 컴파운드 인덱스에서 오름차순과 내림차순을 혼합해서 사용하는 경우는 정렬을 맞춰서 진행해야 한다.
인덱스 스캔의 방향
인덱스의 읽기 방향이 달라지는 것은 주로 쿼리 문장의 정렬 처리나 최대, 최소값 등의 최적화 처리가 수행되는 쿼리이다.
B-Tree 인덱스의 가용성과 효율성
쿼리 조건이나 그룹 또는 정렬에 대한 요건이나 find 에서 가져와야 하는 필드에 따라서 해당 쿼리가 인덱스를 사용할 수 있는지, 사용할 수 있다면 어떠한 비교 조건으로 가능한지 식별할 수 있어야 한다.
이 식별로 조건을 최적화하고 쿼리에 맞게 인덱스를 최적으로 생성한다.
어떤 조건에서 인덱스를 사용할 수 있고, 어떤 경우 사용할 수 없는지 알아본다.
비교 조건의 종류와 효율성
여러 필드로 구성된 컴파운드 인덱스에서 각 필드의 순서와 그 필드에 사용된 조건이 = 의 동등비교인지 > < 인지와 같은 범위비교인지에 따라 각 인덱스 필드의 비교 형태와 효율이 달라진다.
= 와 > < 를 사용하는 쿼리에서
= 이 먼저 나오는 복합 인덱스 경우 작업의 범위를 하나의 값으로 결정하는 조건으로 해당 값만 읽을 수 있지만,
> < 이 먼저 나오는 복합 인덱스 경우 작업의 범위를 제한하지 못하고 단순히 걸러주는 역할만 하는 조건을 필터링(체크) 조건이라고 한다.
작업 범위 결정 조건은 많으면 많을수록 쿼리 처리 성능을 높이지만,
체크 조건은 많으면 많을수록 쿼리 처리 성능을 높이지 못한다.
인덱스의 가용성
B-Tree 인덱스의 특징은 왼쪽 값을 기준으로 오른쪽 값이 정렬되어 있다는 것이다.
즉 왼쪽 값 없이는 인덱스 검색이 불가능하다.
문자열 패턴 일치 검색에서 인덱스를 사용하려면 반드시 정규 표현식이 ^ 로 시작해야 한다.
또 복합 인덱스에서 후순위로 구성된 필드만 조건에서 사용하는 경우 인덱스를 사용할 수 없다.
인덱스는 선행 필드부터 정렬되어 있기 때문이다.
가용성과 효율성 판단
다음과 같은 조건을 위해서는 인덱스를 사용할 수 없다.
여기서 사용할 수 없는 것은 작업 범위 결정 조건으로 사용할 수 없다는 의미로, 경우에 따라 인덱스의 체크 조건으로 사용할 수 있다.
- NOT-EQUAL 로 비교된 경우
- 문자열 패턴 검색에서 프리픽스 일치가 아닌경우
- 문자열 데이터 타입의 콜레시션이 컬렉션이나 인덱스의 콜레이션과 다른 경우
'Database > MongoDB' 카테고리의 다른 글
[MongoDB] 잠금과 트랜잭션(1) (0) | 2024.08.09 |
---|---|
[MongoDB] 인덱스(2) (0) | 2024.08.02 |
[MongoDB] 샤딩(3) (0) | 2024.07.22 |
[MongoDB] 샤딩(2) (0) | 2024.07.05 |
[MongoDB] 샤딩 (0) | 2024.06.28 |