본문 바로가기
Database

MongoDB 성능 개선을 위한 Index 설정

by devLog by Ronnie's 2023. 6. 12.

MongoDB 에서 인덱스를 설정을 하게 되면 보다 빠르게 조회가 가능하다. (다른 DB도 마찬가지) 하지만 인덱스 설정에 따라서 성능의 차이가 있음을 이해하며, 어떤 것들을 고려하며 인덱스를 설정해야 되는지에 대해서 정리한다. 

MongoDB 성능 개선을 위한 Index 설정

 

MongoDB 성능 개선을 위한 Index 설정

 

Index 란?


먼저 들어가기에 앞서 Index에 대해서 가볍게 알아본다.

 

인덱스란 DB의 검색을 빠르게 하기 위해서 데이터의 순서를 미리 정해해 두는 과정을 말한다. 자주 조회되는 필드를 따로 저장해서 조회 및 정렬 시의 속도를 빠르게 하는 기법이다.

 

MongoDB Index?


  • MongoDB에서는 고정된 스키마는 없지만, 원하는 데이터 필드를 인덱스로 지정하여 검색 결과를 빠르게 조회하는 것이 가능하다. 그렇기 때문에 MongoDB를 효율적으로 사용하기 위해서는 인덱스에 대한 이해가 필요하다.
  • 인덱스는 MongoDB 핵심 기능이며, 고성능을 지향하는 MongoDB에서 컬렉션에 적합한 인덱스를 설정하는 것을 성능에 영향을 미치며 데이터가 많아질수록 효과는 커진다.
  • 인덱스는 쿼리에 인덱스만 유효하다. 그렇기 떄문에 두개의 필드에 인덱스가 필요하다면 각각의 인덱스를 만드는 것이 아닌 복합 인덱스를 사용하여 만든다.
  • 인덱스는 어떤 데이터가 추가되거나 수정될 해당 컬렉션에 생성되어 있는 인덱스도 새로운 도큐먼트를 포함시켜 수정된다. 그렇기 때문에 write 작업이 많다면 인덱스를 추가하는 것은 고려해야 한다.

 

 

 

MongoDB Compass


해당 툴은 MongoDB 데이터를 쿼리, 최적화 분석할 있는 혀ㅑ툴이다. 아마 MongoDB 현업에서 사용한다면 해당 툴을 많이 사용할 것이다. 그래서 인덱스 생성 등이나 결과들의 예시를 보여줄 해당 툴에서 제공하는 기능으로 추가적 예시를 예정이다. 

https://www.mongodb.com/try/download/compass

 

Try MongoDB Tools - Download Free Here

Free download for MongoDB tools to do more with your database. MongoDB Shell, Compass, CLI for Cloud, BI Connector and other database tools available.

www.mongodb.com

 

인덱스의 종류


인덱스에도 여러가지 인덱스가 있으며 각 인덱스 종류 별로 알아본다.

 

기본 인덱스 _id

기본적으로 모든 컬렉션은 생성될 때 기본적으로 _id 필드에 대한 인덱스가 부여된다. (여기서 _id는 몽고 DB의 PK)

 

MongoDB는 하나의 컬렉션에 최대 64개의 인덱스까지 생성 가능하다.

 

MongoDB Compass 통해 indexes 탭에 이동해 보면 자동으로 _id 생성되어 있는 것을 확인할 있다.

 

단일 필드 인덱스 (Single Index)

쿼리에서 단 하나의 키만을 이용한다면 단일 필드 인덱스를 사용한다.

 

아래 명령어를 통해 인덱스를 생성할 수 있다. 이때 1은 오름차순을 의미하며, -1로 지정 시 내림차순으로 생성된다. 

db.records.createIndex( { score: 1 } )

 

https://www.mongodb.com/docs/manual/core/index-single/

 

Single Field Indexes — MongoDB Manual

Docs Home → MongoDB Manual MongoDB provides complete support for indexes on any field in a collection of documents. By default, all collections have an index on the _id field, and applications and users may add additional indexes to support important que

www.mongodb.com

 

복합 필드 인덱스 (Compound Index)

아래 명령어를 보면 알겠지만 복합 필드 인덱스는 두개 뿐만이 아니라 두개 이상으로 여러개 필드를 지정하여 인덱스를 생성할 수 있다.

 

이때도 오름차순과 내림차순을 설정할 수 있는데 각각의 정렬을 다르게 생성 할수도 있다.

db.collection.createIndex( { <field1>: <type>, <field2>: <type2>, ... } )

 

https://www.mongodb.com/docs/manual/core/index-compound/

 

Compound Indexes — MongoDB Manual

Docs Home → MongoDB Manual MongoDB supports compound indexes, where a single index structure holds references to multiple fields within a collection's documents. The following diagram illustrates an example of a compound index on two fields:Compound inde

www.mongodb.com

 

Multikey 인덱스

데이터를 다루다보면 필드 타입이 배열인 경우가 있다. 이때 배열 타입에도 인덱스 설정이 가능한데 바로 Multikey 인덱스를 이용하면 된다.

이 인덱스를 통하여 배열에 특정 값이 포함되어 있는 도큐먼트를 조회할 수 있다.

db.coll.createIndex( { <field>: < 1 or -1 > } )

https://www.mongodb.com/docs/manual/core/index-multikey/

 

Multikey Indexes — MongoDB Manual

Docs Home → MongoDB Manual To index a field that holds an array value, MongoDB creates an index key for each element in the array. These multikey indexes support efficient queries against array fields. Multikey indexes can be constructed over arrays that

www.mongodb.com

 

이외에도 2d index internals (지도의 좌표와 같은 데이터), Text indexes (텍스트 관련 데이터), Hashed Indexes (B-Tree가 아닌 Hash 자료구조) 등을 제공한다.

 

인덱스의 종류들을 알아보았고 생성 명령어를 알아보았으니 추가적으로 조회 명령어와 삭제 명령어는 다음과 같다.

 

조회 명령어

db.collection.getIndexes()

 

삭제 명령어

특정 인덱스 삭제 - db.collection.dropIndex(indexName)

전체 인덱스 삭제

db.collection.dropIndexes()

 

 

 

인덱싱 전략


이렇게 여러가지 방법의 인덱스 생성 방법이 있다는 것을 확인했다. 그렇다면 인덱스를 생성할 때 어떻게 생성하는 것이 좋을까? 

 

인덱스 생성 전략에 대해서 알아본다.

 

먼저 기본적인 전략으로는 쓰기 작업이 많은 데이터셋은 인덱스를 복잡하게 설계하지 않는다. 그렇기 때문에 읽기 작업이 많은 곳에서 일단 인덱싱 전략을 잘 세워서 생성을 해줘야 인덱싱 설정에 대한 효과를 볼 수 있다.

 

인덱스 설정 시 고려해야 되는 것들에 대해서 정리한다.

 

 

인덱스 생성 순서와 정렬 순서


먼저 인덱스 생성 순서에 대해서 말해보면 아래와 같이 인덱스를 생성했다고 보자.

db.collection.createIndex({ a: 1, b: 1 })

 

다음과 같이 인덱스를 설정하였을 때 아래 두개의 조회 중 성능이 더 좋은 것은?

db.collection.find().sort({ a: 1, b: 1 }) > db.collection.find().sort({ b: 1, a: 1 })

 

db.collection.find().sort({ a: 1, b: 1 })가 더 좋은 이유는 a와 b 순서에 있다.

 

{ a: 1, b: 1 } 형태로 정의된 인덱스라면 검색을 할때도 해당 순서에 맞는 검색 결과가 더 빨리 조회된다.

 

이것과 마찬가지로 정렬 값도 동일한 결과를 보여준다. 아래와 같은 인덱스를 생성하였을 때

db.collection.createIndex({ a: 1, b: -1 })

 

두가지 형태의 경우는 성능이 좋으며,

db.collection.find().sort({ a: 1, b: -1 })

db.collection.find().sort({ a: -1, b: 1 })

 

아래 두가지 형태의 경우는 성능이 좋지 않다.

db.collection.find().sort({ a: 1, b: 1 })

db.collection.find().sort({ a: -1, b: -1 })

 

이렇듯 이렇게 컴파운드 형태의 인덱스를 생성할 때는 이러한 순서를 고려하여 인덱스를 생성해줘야 한다.

 

db.collection.find().sort({ a: 1, b: 1 }) db.collection.find().sort({ b: 1, a: 1 }) 검색 조건에 따라서 성능이 다르다는 것을 인지해야 한다.

 

explain()


추가적으로 explain() 명령어에 대해서 정리한다. 해당 명령어는 특정 쿼리가 어떻게 수행되었는지에 관하여 설명을 해주는 명령어이다.

 

explain에는 두가지 모드가 있다.

1. executionStats 모드 - 인덱스 사용 여부 / 쿼리의 수행 시간 / 스캔한 문서들의 수

2. allPlansExecution 모드 - 쿼리 계획을 선택하는데 필요한 부분적인 실행 통계

 

자세한 사용 방법은 공식 문서에 잘 정리가 되어 있어 참고 바란다.

https://www.mongodb.com/docs/manual/reference/explain-results/

 

Explain Results — MongoDB Manual

Docs Home → MongoDB Manual To return information on query plans and execution statistics of the query plans, MongoDB provides:Only the most important output fields are shown on this page.The output is subject to change.Some fields are for internal use an

www.mongodb.com

 

인덱스 설정 테스트


이렇게 알아본 인덱스를 설정해보기 위해 인덱스 설정 별로 얼마나 성능의 차이를 보여주는지에 대한 테스트에 대한 내용이다.

 

먼저 인덱스를 설정할 필드는 조회 시 필수적으로 포함을 하는 필드로 인덱스를 설정해줘야 한다.

 

여기서는 예시를 위해 상품을 조회한다고 가정하였을 때 Product Collection의 총 데이터 10만개에서 각 상점별로 1만개 씩의 데이터를 구성하였다. 상점 아이디인 storeId 와 상품의 노출 여부를 결정해주는 displayFlag 필드가 존재한다.

 

기본 조회 쿼리는 다음과 같다.

db.index_test.find({storeId : 1, displayFlag : true}) // 조회 쿼리
db.index_test.find({storeId : 1, displayFlag : true}).explain() // 성능 측정 쿼리

 

자 그럼 이제 인덱스가 없는 경우와 인덱스가 있는 경우, 그리고 인덱스 설정 시 여러가지 설정 케이스 별로 테스트를 진행해본다.

테스트 경우의 수는 아래와 같다.

 

  1. 인덱스 없는 경우
  2. single index (storeId)
  3. single index (storeId) & single index (displayFlag)
  4. compound index (storeId, displayFlag)
  5. compound index (storeId, displayFlag) & single index(storeId)

이때 compound index를 설정 시 주의점이 있다.

 

위에서도 말했듯이 두개 이상의 필드를 사용하는 인덱스를 compound index라 부르는데, 이때 너무 많은 수의 인덱스를 설정하면 좋을 것이라는 생각을 할 수 있지만 보통은 2개 정도를 권장한다고 한다. 이유는 모든 쓰기 작업들이 일어나는 경우에 인덱스의 순서도 재구성을 해야하기 때문에 너무 많은 필드를 통해 인덱스를 설정하게 되면 성능상의 이슈가 있을 수 있기 때문이다.

 

자 그럼 이제 위의 케이스 대로 성능을 측정하였을 때 결과는 어떻게 나왔을까?(아래 시간은 데이터 10만 건을 기준으로 한 결과)

Index Case 조회 시간  
인덱스 없는 경우 1.2초  
single index (storeId) 0.21초 싱글 인덱스만으로도 효과적 확인
single index (storeId) & single index (displayFlag) 0.98초 싱글 인덱스 하나인 경우보다 조회 시간이 오히려 상승한 모습 -> 이유는 교차 인덱스가 발동되서 시간이 더 걸림
compound index (storeId, displayFlag) 0.08초 복합 인덱스 사용 시 조회 성능 훨씬 더 효과적인 모습 확인
compound index (storeId, displayFlag) & single index(storeId) 0.08초 복합 인덱스와 싱글 인덱스 둘 다 존재하여도 복합 인덱스를 사용하는 모습 확인

위에 결과를 보았을 때 알 수 있는 것은 싱글 인덱스만으로도 어느정도 조회 성능 시간이 개선된 모습을 확인 할 수 있으며 추가적으로는 복합  인덱스가 성능적으로는 더 효과적인 모습을 보여준다는 것도 확인할 수 있다.

 

하지만 이것은 어디까지나 해당 케이스일때만의 결과값이고, 현재 인덱스를 설정하고자 하는 도큐먼트에는 위의 정보들을 토대로 여러가지의 케이스를 실행해보면서 인덱스 설정을 해 나가야 된다.

 

 

 

 

 

댓글