PromleeBlog
sitemap
aboutMe

posting thumbnail
MongoDB 성능 최적화 - MongoDB 올인 7일차
MongoDB Performance Tuning - All-in Day 7

📅

🚀

들어가기 전에 🔗

지난 시간에는 MongoDB의 강력한 쿼리 연산자와 집계 프레임워크를 사용하여 원하는 데이터를 효과적으로 찾아내고 분석하는 방법을 배웠습니다.
이제 우리가 만든 애플리케이션이 사용자에게 빠르고 쾌적한 경험을 제공하도록
성능을 관리하고 개선
하는 기술을 익혀볼 차례입니다.


아무리 기능이 좋아도 애플리케이션이 느리다면 사용자는 금방 답답함을 느끼고 떠나버릴 수 있습니다.
특히 데이터 양이 많아질수록 그 중요성은 더욱 커집니다. 오늘은 성능 최적화의 핵심인
인덱싱
쿼리 분석
에 대해 집중적으로 알아보겠습니다.

🚀

왜 느려질까요? 성능 저하의 원인 🔗

MongoDB 성능이 느려지는 데는 여러 이유가 있을 수 있지만, 대표적인 원인은 다음과 같습니다.
이 중에서 우리는 특히
인덱싱
쿼리 최적화
를 통해 성능을 개선하는 방법에 초점을 맞출 것입니다.

🚀

인덱싱 (Indexing) 🔗

성능 최적화의 가장 기본적이면서도 효과적인 방법은 바로
인덱싱
입니다.

인덱싱이란 무엇일까요? 🔗

인덱스는 데이터베이스가 특정 필드의 값을 기준으로 문서들을
빠르게 찾아갈 수 있도록 미리 만들어 놓은 특별한 데이터 구조
입니다.
마치 두꺼운 책의 맨 뒤에 있는
색인(찾아보기)
과 같습니다.
책의 특정 단어를 찾을 때, 색인이 없다면 책 전체를 한 페이지씩 넘겨봐야 하지만, 색인이 있다면 해당 단어가 있는 페이지 번호를 바로 찾아서 훨씬 빠르게 내용을 확인할 수 있습니다.
인덱싱은 페이지가 표시되어있는 목차와 같습니다.
인덱싱은 페이지가 표시되어있는 목차와 같습니다.
MongoDB에서도 인덱스가 없다면, 특정 조건에 맞는 문서를 찾기 위해 컬렉션의
모든 문서
를 하나하나 확인해야 합니다. 이를
컬렉션 스캔(Collection Scan)
이라고 부르며, 데이터 양이 많을수록 매우 느려집니다.
하지만 특정 필드에 인덱스가 있다면, MongoDB는 인덱스를 먼저 참조하여 해당 문서의 위치를 빠르게 파악하고 접근할 수 있습니다. 이를
인덱스 스캔(Index Scan)
이라고 합니다.

MongoDB 인덱스 종류 🔗

MongoDB는 다양한 종류의 인덱스를 지원합니다. 자주 사용되는 주요 인덱스들은 다음과 같습니다.
➡️

1. 단일 필드 인덱스 (Single Field Index) 🔗

가장 기본적인 형태로,
하나의 필드
에 대해 생성하는 인덱스입니다.
예를 들어, 'users' 컬렉션에서 'email' 필드로 사용자를 자주 검색한다면, 'email' 필드에 단일 필드 인덱스를 생성할 수 있습니다.
// users 컬렉션의 email 필드에 오름차순(1) 인덱스 생성
db.users.createIndex({ email: 1 })
(오름차순(1) 또는 내림차순(-1) 지정은 단일 필드 인덱스에서는 쿼리 성능에 큰 영향을 주지 않지만, 복합 인덱스에서는 중요합니다.)
➡️

2. 복합 인덱스 (Compound Index) 🔗

두 개 이상의 필드
를 조합하여 생성하는 인덱스입니다. 여러 필드를 조건으로 사용하는 쿼리의 성능을 높이는 데 효과적입니다.
예를 들어, 사용자를 'status' 필드와 'age' 필드로 함께 검색하는 경우가 많다면, 두 필드를 묶어 복합 인덱스를 만들 수 있습니다.
// users 컬렉션의 status 필드(오름차순)와 age 필드(내림차순)로 복합 인덱스 생성
db.users.createIndex({ status: 1, age: -1 })
👨‍💻
복합 인덱스에서는 필드의 순서가 매우 중요합니다.
위 예시처럼 { status: 1, age: -1 } 로 인덱스를 생성하면, 다음과 같은 쿼리들에 효율적으로 사용될 수 있습니다.
하지만 age 필드만으로 검색하는 쿼리에는 이 복합 인덱스가 효율적으로 사용되기 어렵습니다. (첫 번째 인덱스 키인 status 가 쿼리에 포함되지 않았기 때문입니다.)
따라서
쿼리에서 자주 사용되는 필터링 및 정렬 순서
를 고려하여 복합 인덱스의 필드 순서를 신중하게 결정해야 합니다.
➡️

3. 기타 주요 인덱스 🔗

인덱스 생성 및 관리 🔗

MongoDB Shell에서 인덱스를 다루는 주요 명령어는 다음과 같습니다.

인덱스 사용 시 주의할 점 🔗

인덱스는 쿼리 성능을 크게 향상시키지만, 만능은 아닙니다. 다음과 같은 점들을 유의해야 합니다.

🚀

쿼리 최적화 🔗

인덱스를 잘 만들었더라도 쿼리 자체가 비효율적이라면 성능 문제가 발생할 수 있습니다. MongoDB는 쿼리가 내부적으로 어떻게 실행되는지 분석할 수 있는 강력한 도구를 제공합니다.

쿼리 실행 계획 분석: explain() 🔗

explain() 명령어는 find, aggregate, update, delete 등 다양한 명령어 뒤에 붙여서 해당 작업이 어떻게 실행될 것인지(또는 실제로 어떻게 실행되었는지)에 대한
실행 계획
정보를 보여줍니다.
// find 쿼리의 실행 계획 보기
db.users.find({ status: "active", age: { $gt: 30 } }).explain()
 
// aggregate 파이프라인의 실행 계획 보기
db.users.aggregate([...pipeline...]).explain()
explain() 명령어는 여러 수준의 상세 정보(verbosity mode)를 제공합니다. 일반적으로 'executionStats' 모드를 사용하여 실제 실행 통계까지 확인하는 것이 유용합니다.
db.users.find({ ... }).explain("executionStats")
explain() 사용 예시
explain() 실행 예시
myPracticeDB> db.users.find({ status: "active", age: { $gt: 30 } }).explain("executionStats")
{
  explainVersion: '1',
  queryPlanner: {
    namespace: 'myPracticeDB.users',
    parsedQuery: {
      '$and': [ { status: { '$eq': 'active' } }, { age: { '$gt': 30 } } ]
    },
    indexFilterSet: false,
    queryHash: '3C18BE05',
    planCacheShapeHash: '3C18BE05',
    planCacheKey: '16EDF2A1',
    optimizationTimeMillis: 0,
    maxIndexedOrSolutionsReached: false,
    maxIndexedAndSolutionsReached: false,
    maxScansToExplodeReached: false,
    prunedSimilarIndexes: false,
    winningPlan: {
      isCached: false,
      stage: 'COLLSCAN',
      filter: {
        '$and': [ { status: { '$eq': 'active' } }, { age: { '$gt': 30 } } ]
      },
      direction: 'forward'
    },
    rejectedPlans: []
  },
  executionStats: {
    executionSuccess: true,
    nReturned: 0,
    executionTimeMillis: 0,
    totalKeysExamined: 0,
    totalDocsExamined: 3,
    executionStages: {
      isCached: false,
      stage: 'COLLSCAN',
      filter: {
        '$and': [ { status: { '$eq': 'active' } }, { age: { '$gt': 30 } } ]
      },
      nReturned: 0,
      executionTimeMillisEstimate: 0,
      works: 4,
      advanced: 0,
      needTime: 3,
      needYield: 0,
      saveState: 0,
      restoreState: 0,
      isEOF: 1,
      direction: 'forward',
      docsExamined: 3
    }
  },
  queryShapeHash: '87856776218A3BB688005500CB2DB5CAC0C1CFDF3DEAF739549229E4718056FC',
  command: {
    find: 'users',
    filter: { status: 'active', age: { '$gt': 30 } },
    '$db': 'myPracticeDB'
  },
  serverInfo: {
    host: 'LDH',
    port: 27017,
    version: '8.0.8',
    gitVersion: '7f52660c14217ed2c8d3240f823a2291a4fe6abd'
  },
  serverParameters: {
    internalQueryFacetBufferSizeBytes: 104857600,
    internalQueryFacetMaxOutputDocSizeBytes: 104857600,
    internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,
    internalDocumentSourceGroupMaxMemoryBytes: 104857600,
    internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,
    internalQueryProhibitBlockingMergeOnMongoS: 0,
    internalQueryMaxAddToSetBytes: 104857600,
    internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600,
    internalQueryFrameworkControl: 'trySbeRestricted',
    internalQueryPlannerIgnoreIndexWithCollationForRegex: 1
  },
  ok: 1
}

결과 해석하기 🔗

explain() 결과는 JSON 형태로 다양한 정보를 담고 있지만, 성능 분석 시 특히 주목해야 할 부분은 다음과 같습니다.
IXSCAN
IXSCAN
COLLSCAN(dropIndex 후)
COLLSCAN(dropIndex 후)
위 예시에서 IXSCAN을 사용한 쿼리와 COLLSCAN을 사용한 쿼리의 결과를 비교해보면, totalDocsExamined 수가 IXSCAN에서는 1인 반면, COLLSCAN에서는 3으로 나타납니다. 이는 인덱스를 사용하지 않고 컬렉션 전체를 스캔했음을 의미합니다.
핵심은 totalDocsExamined 수가 nReturned 수에 비해 얼마나 많은지를 보는 것입니다. 만약 인덱스를 잘 사용했다면 (IXSCAN), 이 두 값의 차이가 크지 않을 것입니다. 반면 컬렉션 스캔(COLLSCAN)이 발생하면 totalDocsExamined 수가 매우 커지고 실행 시간도 길어질 가능성이 높습니다. COLLSCAN이 발견되면 해당 쿼리에 적합한 인덱스를 생성하거나 쿼리 자체를 수정하는 것을 고려해야 합니다.

쿼리 최적화 팁 🔗


🚀

지속적인 관리: 모니터링 및 성능 관리 도구 🔗

성능 최적화는 한 번으로 끝나는 것이 아니라, 서비스 운영 중에 지속적으로 상태를 확인하고 관리하는 과정이 필요합니다. MongoDB는 성능 모니터링을 위한 여러 도구와 방법을 제공합니다.

🚀

결론 🔗

오늘은 MongoDB 애플리케이션의 성능을 높이는 핵심적인 방법들을 배웠습니다.
특히 쿼리 속도를 획기적으로 개선할 수 있는
인덱싱
의 개념과 종류, 사용법, 주의사항을 자세히 살펴보았고, explain 명령어를 사용하여
쿼리 실행 계획을 분석
하고 문제점을 찾아내는 방법을 익혔습니다. 또한 지속적인 성능 관리를 위한 모니터링 도구들도 간략히 소개했습니다.
성능 최적화는 개발 초기뿐만 아니라 서비스 운영 중에도 꾸준히 관심을 가지고 관리해야 하는 중요한 영역입니다. 오늘 배운 내용들을 바탕으로 여러분의 MongoDB 기반 애플리케이션이 항상 최상의 성능을 유지할 수 있도록 노력해 보세요.
다음 시간에는 MongoDB의 고급 기능들 중 하나인
복제 세트(Replica Set)
에 대해 더 깊이 알아보고, 실제로 어떻게 구성하고 장애 상황에 어떻게 대처하는지 살펴보겠습니다. 데이터의 안정성과 가용성을 높이는 중요한 내용입니다.

참고 🔗