Contents

Mongodb Index Strategy

MongoDB에 저장되는 다큐먼트 데이터들에는 반드시 인덱스를 한 개 이상 사용하는 것이 성능 향상적 측면에서 권장된다.

그렇다면 인덱스는 언제, 어떻게, 무엇을 사용해야 할까? MongoDB의 인덱스 전략에 대해 정리해보았다.

https://www.mongodb.com/docs/manual/applications/indexes/

ESR Rule

Compound Index를 사용하는 경우 여러 필드에 대한 레퍼런스 자체를 인덱스로 사용할 수 있고, 때문에 응답 시간을 비약적으로 향상시킬 수 있는 인덱스 종류이다.

그런데 인덱스에 적용되는 필드 순서도 중요하다.

Equality

Equality는 단일 값과 정확하게 매칭되는 개념을 의미한다.

  • 인덱스 검색은 정확히 일치하는 항목을 효율적으로 사용한다.
  • 인덱스 검색은 때문에 쿼리를 충족하기 위해 검사해야 하는 문서 개수를 제한한다.

정확히 일치해야 하는 (Equality) 필드를 인덱스의 가장 먼저 배치해라.

인덱스에는 정확히 일치하는 쿼리를 위한 여러 개의 키가 존재할 수 있다.

  • Equality를 만족하기 위한 인덱스 키는 어떤 순서로든 표시될 수 있다.
  • 하지만 인덱스와 Equality를 충족하려면 모든 Equality를 위한 인덱스 키가 다른 인덱스 필드보다 앞에 와야 한다.
  • MongoDB의 검색 알고리즘은 정확히 일치하는 필드를 특정 순서로 정렬할 필요가 없다.

정확히 매칭되는 항목들은 selective 해야 한다. 스캔 대상이 되는 인덱스 키들을 줄이기 위해서는 Equality 테스트가 90% 이상의 가능한 도큐먼트 매치를 줄일 수 있어야 한다.

Sort

정렬은 쿼리 결과의 순서를 결정한다.

정렬은 동일 항목이 일치하면 정렬해야 하는 문서 수가 줄어들기 때문에, Equality를 통해 동일 항목에 대한 일치가 발생한 이후에 수행한디.

또한 Equality 일치를 수행한 이후에는 MongoDB가 논블락킹 정렬을 수행할 수 있다는 장점도 있다.

인덱스는 쿼리 필드가 인덱스 키의 하위 집합인 경우 정렬 작업을 지원한다.

인덱스 키의 하위 집합에 대한 정렬 연산은 쿼리가 정렬 키 앞에 있는 모든 prefix 키들에 대한 equality 조건이 포함되는 경우에만 지원된다.

Range

Range는 스캔한 필드를 필터링한다.

  • 이 스캔은 정확한 매칭을 필요로 하지 않는다.
  • Range Filter는 인덱스 키에 느슨하게 바인딩된다. (loosely bound)

쿼리 효율성을 높이려면 범위 바운드를 최대한 엄격하게 설정하고, equality 매칭을 사용해서 스캔 대상이 되는 문서 수를 줄이는 것이 중요하다.

MongoDB는 range 필터의 결과에 대한 인덱스 정렬을 수행할 수 없다.

그러므로 Range 필터를 정렬 다음에 배치하여 MongoDB가 논블락킹 인덱스 정렬을 수행할 수 있도록 해라.

인덱스가 RAM에 들어가도록 할 것

빠른 처리를 위해서는 인덱스들이 RAM에 딱 들어맞게 설계해야 한다. 그래야 시스템이 디스크로부터 인덱스를 읽어오는 일을 피할 수 있다.

인덱스의 크기를 살펴보려면, db.collection.totalIndexSize() 헬퍼를 사용하면 된다.

만약 현재 사용 중인 인덱스의 크기를 RAM 크기에 맞추려면

  • 해당 용량 이상의 RAM을 사용해야 한다.
  • 나머지 working set에 사용할 수 있는 RAM도 있어야 한다.

여러 컬렉션을 사용하는 경우 모든 컬렉션에 있는 모든 인덱스의 크기를 고려해야 한다.

인덱스와 working set들은 동시에 메모리에 들어갈 수 있어야 한다.

모든 인덱스가 RAM에 들어가야 하나요?

모든 인덱스들이 RAM에 다 들어가 있어야 하는 것은 아니다.

만약 인덱싱된 필드의 값이 매 insert 마다 증가하고, 대부분의 쿼리들은 가장 최근 추가된 다큐먼트에 대한 select 작업을 수행한다면,

MongoDB 입장에서는 인덱스의 부분만 가지고 있으면 될 것 같다.

  • 가장 최근의 데이터에 대한 인덱스나
  • RAM의 right-most한 값이다.

이러한 접근 방법은 read와 write 작업에 대해 효율적인 인덱스 사용을 하게 해주고 인덱스 사용을 지원하기 위한 RAM 용량을 최소화할 수 있다는 장점이 있다.

selectivity를 보장하기 위한 쿼리

Selectivity (선택성)은 쿼리가 인덱스를 사용해서 결과의 범위를 좁힐 수 있는 기능이다.

효과적인 인덱스는

  • 선택성이 뛰어나고
  • 쿼리 수행과 관련된 작업의 더 많은 부분에 인덱스를 사용할 수있다.

선택성을 보장하려면 인덱싱된 필드로 가능한 문서 수를 제한하는 쿼리를 작성해라.

예시

{ _id: ObjectId(), a: 1, b: "ab" }
{ _id: ObjectId(), a: 1, b: "cd" }
{ _id: ObjectId(), a: 1, b: "ef" }
{ _id: ObjectId(), a: 2, b: "jk" }
{ _id: ObjectId(), a: 2, b: "lm" }
{ _id: ObjectId(), a: 2, b: "no" }
{ _id: ObjectId(), a: 3, b: "pq" }
{ _id: ObjectId(), a: 3, b: "rs" }
{ _id: ObjectId(), a: 3, b: "tv" }

상기 컬렉션에서 { a: 2, b: "no" }를 검색했을 때에는 3개의 document를 검색해야 한다.

{ a: { $gt: 1 }, b: "tv" }는 6개의 도큐먼트를 스캔하고 하나의 결과를 리턴한다.

{ _id: ObjectId(), a: 1, b: "ab" }
{ _id: ObjectId(), a: 2, b: "cd" }
{ _id: ObjectId(), a: 3, b: "ef" }
{ _id: ObjectId(), a: 4, b: "jk" }
{ _id: ObjectId(), a: 5, b: "lm" }
{ _id: ObjectId(), a: 6, b: "no" }
{ _id: ObjectId(), a: 7, b: "pq" }
{ _id: ObjectId(), a: 8, b: "rs" }
{ _id: ObjectId(), a: 9, b: "tv" }

그에 반해 상기 컬렉션에서 { a: 2, b: "cd" }를 검색했을 때에는 쿼리에 대한 결과를 리턴하기 위해 하나의 다큐먼트만 스캔한다.

2번 컬렉션의 인덱스와 쿼리가 a에 대한 값이 고르게 분산되어 있고 쿼리가 인덱스를 사용해서 특정한 다큐먼트를 select 할 수 있기 때문에 더 selective하다.

selectivity가 낮고, MongoDB가 결과를 위해 다수의 다큐먼트를 읽어야 하는 것이 예상되는 상황이라면 인덱스를 사용하지 않는 것이 때로는 더 빠를 때가 있다.