인덱스
역시 다른 DB와 같이 인덱스를 생성하는게 보통이다. mongo에서 인덱스는 1) 단순(simple) 인덱스, 2) 복합(compound) 인덱스 가 있다.
- 단순 인덱스 : 단순 하나의 요소만 잡은 인덱스다.
- 복합 인덱스 : 여러 요소를 하나의 키로 묶은 인덱스다. 쿼리당 하나의 인덱스만 사용할 수 있기에 인덱스를 두 개 이상의 필드에 태우려면 필요하다. 또한 선언 순서가 중요하다.
효율
쿼리 성능을 위해서는 인덱스가 반드시 필요하지만 인덱스에는 유지 비용이 들어간다. 도큐먼트를 추가할 때마다 컬렉션에 생성된 인덱스도 새로운 도큐먼트를 포함시켜야 한다.
물론 읽기 위주라면 감당할 수 있는 비용이고 상쇄할 수 있다. 문제는 인덱스가 적합하더라도 메모리에 다 올리지 못해서 페이지 폴트가 생길 때 문제다.
램에 없어서 가지러 들어가고 올려놓고를 반복한다. 위와 같은 상황을 Threshing이라고 한다. 그래서 인덱스를 너무 많이 작성하는 것도 그리 바람직하지 않다.
B-Tree
Mongo index도 B-Tree다. 트리 유사한 구조로 키의 삭제, 추가에 따라 밸런싱을 한다. 또한 일치, 범위, 정렬, 전방일치 등을 용이하게 할 수 있다.
타입
- unique : 인덱스의 카디널리티가 높다. 선택도가 1이다. (선택도 = 카디널리티 / 전체 레코드 수)
db.users.createIndex({userName:1}, {unique:true, dropDups: true}) //삭제 명령도 내릴 수 있다.
- 희소 인덱스 vs. 밀집 인덱스
- 희소 인덱스 : 인덱스는 레코드 포인터를 갖는데 같은 인덱스를 가진 레코드들 중 첫 번째 만 갖고 있는 것을 의미한다. 클러스터링 인덱스에서 사용할 수 있다.
- 밀집 인덱스 : 인덱스는 레코드 포인터를 갖는데 같은 인덱스를 가진 레코드들의 레코드 포인터를 하나씩 모두 갖고 있는 경우를 밀집 인덱스라고 한다. 넌클러스터링
생성
db.target.createIndex({name: 1}, {background: 1}) //1 : asc, -1 : desc
다중키 인덱스
필드 값이 배열이고, 이를 인덱스로 잡는 경우를 다중 키 인덱스(multiKey Index)라고 한다. 이렇게 구성하면 인덱스 내의 여러 개의 엔트리가 동일한 도큐먼트를 지시하게 된다. 예를 들어보자.
{
name: "Apple",
tags: ["iPhone", "iPad", "appleWatch", "visionPro", "macbookPro", "macbookAir"]
}
tags에 다중키 인덱스를 잡았다면 여러 개의 인덱스 엔트리가 같은 도큐먼트를 지시하고 있다. 이와 같이 배열 내 요소는 각자 자신만의 엔트리를 가지고 있다. 이렇게 인덱스를 사용할 수 있지만 역시나 비용은 만만치 않다.
해시 인덱스
해시 함수를 통해서 인덱스를 구성하는 경우를 해시 인덱스라고 한다.
db.table.createIndex({element:'hashed'})
와 같이 구성할 수 있다. 해시 인덱스는 원본을 해싱하므로 몇 가지 제약 사항이 있다.
- $eq 쿼리는 동일하게 동작하지만 범위 쿼리는 지원하지 않는다.(해싱되었으므로 원본 값을 가지고 범위 쿼리는 불가능하다.)
- 다중 키 해시 인덱스는 허용되지 않는다.
- 부동소수점은 해시되기 전에 정수로 변환된다.
해시 인덱스는 인덱스의 엔트리가 균등하게 분배된다는 장점이 있다.
관리
생성, 삭제
createIndex()로 생성한다. (db.target.createIndex({indexName:1}))db.system.indexes.find().pretty()로 인덱스를 검증할 수 있다.- deleteIndexes로 삭제할 수 있다. (
db.runCommand({deleteIndexes: "user", index: "zip"})) dropIndex()로 삭제할 수 있다. (db.target.dropIndex("indexName"))
선언
인덱스 만들기가 굉장히 쉽다. 그래서 러닝 중이던 준비 중이던 인덱스를 잡아야 할 떄가 생길 수 있다. 그러나, 러닝 중이라면 더 고민해보자. 인덱스 구축은 오랜시간이 걸린다. 일종의 마이그레이션으로 봐도 크게 다르지 않을 수 있다.
과정 (인덱스 구축)
- 인덱스할 값을 정렬한다.
- B-Tree에 추가된다.
- 정렬된 값들이 인덱스로 삽입된다.
이 과정이 데이터 수가 많다면 오래 걸릴 수가 있다.
백그라운드 인덱싱
그래서 인덱스 구축을 백그라운드에서 할 수도 있다. 물론 쓰기 잠금은 걸리지만 중간중간 읽기, 쓰기를 허용하기 위해서 잠시간 멈추면서 진행된다.
db.target.createIndex({key: 1}, {background: true})로 백그라운드에서 인덱스를 생성할 수 있다.
리인덱싱
기존 데이터의 업데이트, 삭제가 대량으로 발생하면 아무리 인덱스가 재구성한다고 하더라도 단편화는 피할 수 없다. 이런 경우 인덱스를 재구축할 필요가 생긴다.
db.target.reIndex()