인덱스
인덱스는 MongoDB 의 쿼리를 더 효과적으로 수행할수 있게 도와주는 기능이다. Document 를 주어진 조건에 맞게 색인 한다음, 해당 색인을 기준으로 데이터를 조회 하는 방식이다. 만약 이 인덱스가 존재 하지 않는다면, 마치 특정 단어를 찾기위해 사전의 첫장부터 단어가 나올때까지 한 단어씩 검색하는 FULL-SCAN 방식으로 조회 해야 하기때문에 시간과 비용이 많이 발생 한다.
MongoDB 는 Document의 특정 부분 (지정한 부분) 을 B-TREE 구조로 저장 해둔다. 그리고 저장된 값을 이용해 데이터를 조회, 정렬한다. B-TREE 는 이진 트리 형태로 데이터를 저장하는 방식이다. 각 값들은 자기보다 작은 값을 앞에, 자신보다 큰값을 뒤에 저장한다. 자세한 B-TREE 설명은 따로 검색해보자. >_<
인덱스 생성시 주위점
인덱스는 검색을 빠르게 해주는 장점이 있으나, Document 생성시 인덱스 색인이 필요하므로 쓰기 성능은 인덱스 수에 반비례로 줄어든다.
또 MongoDB 는 Document, Index를 램에 스왑 하여 READ WRITE 하기때문에 Document, Index 의 데이터가 램보다 커지면, Disk Access 가 일어나 성능이 저하된다. 이를 스래싱(thrasing) 이라고 한다.
때문에 인덱스는 꼭 필요한 만큼만 생성 해야 한다.
인덱스 생성
인덱스 생성은 다음과 같이 createIndex 명령어로 생성한다.
db.collection.createIndex( <key and index type specification>, <options> )
첫번째 항목은 지정해야할 key와 정렬 순서를 지정한다.
지정할 키를 먼저 지정하고, 정렬값을 value 로 지정한다. 정렬값은 1이 오름차순, -1이 내림차순이다.
인덱스 명도 지정할 수 있는데, 두번째 옵션에 name 값에 지정할 인덱스 명을 명시한다.
item 값은 오름차순, quantity 는 내림차순으로 복합키 인덱스로 지정하고, 해당 인덱스 명은 "query for inventory" 로 지정하고자 한다면 다음과 같이 입력한다.
db.products.createIndex(
{ item: 1, quantity: -1 } ,
{ name: "query for inventory" }
)
인덱스 종류와 생성
Single Field Index (단일키 인덱스)
일반적으로 하나의 키를 이용한 인덱스이다. _id 가 가장 대표적인 예이다. 하나의 값을 색인값으로 지정하고, 또 해당 값이 uniue 해야 하는지도 지정 가능하다. Document 생성시 _id 는 default 로 Single Field Index 로 지정되며 unique 옵션이 지정된다.
생성은 다음과 같이 키를 하나만 지정하며, 고유값으로 지정하기 위해서 unique 값에 true, 그렇지 않으면 false 를 입력한다.
db.records.createIndex( { score: 1 } , {unique: true})
고유값으로 지정하두고, 중복된 value 를 입력하면, update 혹은 insert 시 dup key 오류가 발생 한다.
고유키 인덱스는 Document 입력 전에 생성하는게 좋은데, 만약 중복키가 존재 할경우 index 생성시 오류가 발생한다.
이때 옵션값으로 {unique: true, dropDups: true} 와 같이 dropDups 를 true 로 지정하면 중복 데이터를 임의로 삭제한뒤 index를 생성한다.
Sparse Index (희소 인덱스)
희소 인덱스는 index 에 지정한 값이 Document에 존재 하지 않을 경우 해당 값을 색인 하지 않는 기능을 말한다.
일반적으로 인덱스 생성시 Document 에 지정한 키 값이 존재 하지 않으면 null 로 색인하고 조회시 null 값을 이용해 데이터를 탐색한다. 대부분의 index key 가 null 인 컬렉션에 index 를 생성시 null 로 색인된 데이터가 많다면 매우 비효율 적일 것이다. 이때 희소 인덱스 옵션을 지정하여 Index 키가 존재 하지 않는 Document 는 색인 대상에서 제외 할 수 있다. 해당 다음과 같이 옵션은 sparse 를 이용해 지정한ㄷ다.
db.addresses.createIndex( { "xmpp_id": 1 }, { sparse: true } )
Muilkey Index (다중키 인덱스)
멀티키 인덱스는, 고정된 배열 값에 대해 인덱스를 지정하는 방식을 말한다.
인덱스를 생성하는 방식은 Single Field Index 와 동일하다. 키 값만 다중으로 존재 하는 것이므로 배열 내에 어떤 값도 해당 Document 를 똑같이 지정 하게 된다.
compound key index (복합키 인덱스)
복합키 인덱스는 두개 이상의 키를 인덱스로 지정하는 방식이다.
생성은 다음과 같이 필요한 키를 나열하면서 지정한다.
db.collection.createIndex( { <field1>: <type>, <field2>: <type2>, ... } )
복합키 인덱스는 키가 조합된 인덱스로, 순서가 중요하다. 두개의 키로 조합된 만큼 두 값 모두 입력이 되었을때 가장 효과가 좋다.
복합키 인덱스로 두개의 키를 기준으로 생성하고 조회 하는 방식은 다음과 같은 케이스가 있을 수 있다.
첫번째 케이스는 두 값을 모두 지정하는 경우이다. 두말할것 없이 이 경우가 가장 이상적인 경우이다.
두번째 케이스는 특정 키 값만 지정 하는 경우이다. 일반적으로는 두개 이상의 복합키로 조건을 제시 하기 위해서 복합키 인덱스를 사용하지만, 카테고리 조회 같은 경우 - 대 중 소 카테고리중 상위 조건으로만 조회 하고 싶을 경우에 하위 조건을 입력 하지 않아야 할 니즈도 분명 발생한다. 이때는 필수로 키가 존재 하는 순서대로 인덱스를 생성 해야 한다.
예를 들어 다음과 같이 상품의 카테고리 대, 중, 소 가 존재한다고 하자.
대 | 중 | 소 |
과일 | 사과 | 아오리 |
과일 | 사과 | 부사 |
과일 | 포도 | 샤인머스켓 |
과일 | 포도 | 대봉 |
위 카테고리에 복합키 인덱스로 대,중,소 가 차래대로 입력 되었다고 가정하자. 이때 과일 카테고리의 모든 상품을 조회 하기 위해서, 대 카테고리 키에 과일만 입력 하면 지정된 복합키 인덱스를 이용해 조회가 가능하다.
인덱스시 생성시 MongoDB는 다음과 같이 인덱스를 생성 해두기때문에 과일만으로도 생성해둔 인덱스 엔트리 검색이 가능하다.
과일 - 사과 - 아오리 | 0X01 |
과일 - 사과 - 부사 | 0X02 |
과일 - 포도 - 샤인머스켓 | 0X03 |
과일 - 포도 - 대봉 | 0X04 |
하지만 중분류 만 조회 할 경우에는 해당 인덱스 탐색이 불가능하다. 인덱스 엔트리에는 대분류 키기준에서 순차적으로 인덱싱이 되어 있기 때문이다.
세번째 케이스는 범위 조건이다.
상품명 | 가격 |
아오리 | 2000 |
부사 | 1200 |
샤인머스켓 | 3000 |
대봉 | 4200 |
범위 조건일 경우, 범위가 되는 키는 뒤에 지정 되어야 한다. 인덱스는 선행키가 최대한의 탐색 범위를 줄여야 하기때문에 상대적으로 수를 줄일수 있는 탐색 조건이 첫번째로 지정 되어야 하며 두번째는 값의 범위가 상대적으로 다양한 값이 을 뒤로 두어야 한다.
Hashed Index (해시 인덱스)
Hashed Index 는 인덱스 필드의 값을 해쉬 하여 관리하다. 해시 인덱스는 해시된 키를 이용해 샤딩을 지원하다. 해시 기반의 샤딩은 해시 인덱스를 샤드 키로 하여, 샤드된 클러스타 파티션에 데이터를 분할한다. 때문에 MongoDB 를 구성시 Hashed Index 는 필수적이다.
해시 키는 다음과 같이 생성한다. 복합 해시 키는 4.4 버젼 이후 부터 지원된다.
// Hashed Index
db.collection.createIndex( { _id: "hashed" } )
// Compound Hashed Index
db.collection.createIndex( { "fieldA" : 1, "fieldB" : "hashed", "fieldC" : -1 } )
해시키 인덱스는, 다음과 같은 제한사항이 있다.
- 범위조건은 조회 허용 되지 않는다.
- 부동 소숫점은 정수로 전환된다.
때문에 해당 조건에 맞게 적절 하게 사용 되어야 한다.
Text Index (텍스트 인덱스)
Text Index 는 문자열 컨텐츠 검색을 위한 인덱스 이다. 이 index 는 문자나, 문자 배열 모두 지원한다.
단 text 인덱스는 collection 당 하나의 인덱스 생성만 가능하다.
생성은 컬럼 값을 "text" 로 지정하여 생성한다.
// single field index
db.reviews.createIndex( { comments: "text" } )
// compound index
db.reviews.createIndex(
{
subject: "text",
comments: "text"
}
)
// TODO
Wildcard index (와일드카드 인덱스)
// TODO
인덱스 기능
Index Intersection (인덱스 교차)
Index Intersection (인덱스 교차)는 두개의 인덱스를 동시에 사용해 교집합 데이터를 조회 하는 기능이다.
커버링 인덱스
인덱스 관리
인덱스 조회
인덱스 조회는 getIndexes 를 이용해 조회 할 수 있다.
db.people.getIndexes()
인덱스 삭제
인덱스 삭제는 다음 두가지 명령어로 삭제 가능하다.
db.collection.dropIndex( { 'index-name' : 1 } )
db.collection.dropIndexes()
dropIndex 는 인덱스 명을 지정하여 해당 인덱스를 삭제할 수 있다.
dropIndexes 는 모든 인덱스를 삭제 할 수 있다. 단, _id 인덱스는 삭제 되지 않는다.
4.4 버전 부터는 dropIndexes 에서는 생성중인 index 도 중지 시킨다.
기타
인덱스 생성 시점
유니크 인덱스는 Collection 생성 시점 전에 만들어야 중복 이슈를 해결 할수 있지만, 일반적으로 대부분은 Document 가 모두 입력된 뒤 생성하는것이 이상적인 Index 엔트리를 생성하는데 더 도움이 된다.
또, 모두 개발 한 뒤에 인덱스를 생성해야 필요한 인덱스를 정리해 중복되지 않게 인덱스를 생성 할 수 있다.
Background Index Create (백그라운드 인덱스 생성)
인덱스 생성은 데이터를 순차적으로 색인 하는 것으로, 인덱스 생성시에 write 가 발생하면 색인에 방해가 된다. 그래서 MongoDB 는 INDEX 생성시 Collection 에 lock 을 걸어두고 인덱스를 생성한다.
하지만 운영중인 시스템에서 lock 발생시 어플리케이션이 중지가 발생 할 수 있기때문에 MongoDB 에서는 background 에서 인덱스를 생성 할 수 있도록 기능을 제공 한다.
db.addresses.createIndex( { "xmpp_id": 1 }, { background: true } )
인덱스 생성시 두번째 옵션 값으로 background 를 지정하면, write 가 발생 했을때 MongoDB 는 인덱스 생성을 잠시 멈추고 WRITE 를 수행한뒤 다시 INDEX 를 생성 한다. 때문에 WRITE 작업이 많으면 많을 수록 INDEX 생성 속도가 느려지는 단점이 있지만, 서비스를 중단 하지 않고 INDEX 를 생성 할 수 있기때문에 매우 유용하다.