MongoDB 성능 모니터링: 주요 명령어와 메트릭 설명

필수 셸 명령어를 사용하여 MongoDB 성능을 사전에 모니터링하는 방법을 알아보세요. 이 가이드에서는 `db.currentOp()`와 `db.serverStatus()`를 통한 연결 상태 추적, 프로파일링 명령어(`db.setProfilingLevel`)를 사용한 느린 쿼리 분석, 리소스 사용률 및 인덱스 상태와 관련된 주요 메트릭을 해석하여 최적의 데이터베이스 튜닝을 수행하는 방법을 자세히 설명합니다.

MongoDB 성능 모니터링: 주요 명령어와 메트릭 설명

효과적인 데이터베이스 관리는 강력한 모니터링에 달려 있습니다. 선도적인 NoSQL 문서 데이터베이스인 MongoDB의 경우, 성능 메트릭을 이해하는 것은 높은 가용성과 응답성을 유지하는 데 중요합니다. 느린 쿼리, 과도한 리소스 소비 또는 예상치 못한 연결 급증은 애플리케이션 성능에 심각한 영향을 미칠 수 있습니다.

MongoDB가 느려졌을 때, 가장 먼저 떠오르는 유용한 질문은 "데이터베이스가 나쁜가?"가 아닙니다. "서버가 지금 무엇을 하고 있으며, 이것이 정상과 다른가?"입니다. 아래 명령어들은 인덱스를 변경하거나, 하드웨어 크기를 조정하거나, 애플리케이션을 비난하기 전에 제가 첫 번째로 사용하는 명령어들입니다.

MongoDB 셸(mongosh)의 필수 모니터링 명령어

이러한 명령어를 실행하기 위한 기본 인터페이스는 MongoDB 셸(mongosh) 또는 레거시 mongo 셸입니다. 여기에 표시된 모든 명령어는 이 셸 환경 내에서 실행됩니다.

1. 현재 연결 이해: db.currentOp()db.serverStatus()

활성 연결을 모니터링하는 것은 연결 고갈을 방지하고 리소스를 차단할 수 있는 장기 실행 작업을 식별하는 데 중요합니다.

db.currentOp()

이 명령어는 데이터베이스에서 현재 실행 중인 작업에 대한 정보를 반환합니다. 실시간으로 느리거나 차단된 쿼리를 식별하는 데 필수적입니다.

사용 예:

현재 실행 중인 모든 작업을 보려면:

db.currentOp()

특정 임계값보다 오래 실행 중인 작업(예: 5초 이상 실행 중인 작업)을 찾으려면:

db.currentOp({"secs_running": {$gt: 5}})

출력에는 op, ns(네임스페이스), query, secs_running과 같은 세부 정보가 포함됩니다.

db.serverStatus()

이 명령어는 포괄적인 상태 정보를 제공하지만, connections 섹션은 연결 풀링 및 제한을 모니터링하는 데 중요합니다.

serverStatus 내 주요 메트릭 (연결 섹션):

  • current: 서버에 대한 활성 연결 수입니다.
  • available: 설정된 최대값에 따라 설정할 수 있는 사용 가능한 연결 수입니다.
db.serverStatus().connections

2. 쿼리 성능 분석: db.getProfilingStatus()db.setProfilingLevel()

MongoDB는 데이터베이스 작업의 실행 세부 정보를 기록하는 내장 프로파일링 도구를 제공하므로 리소스를 많이 사용하는 쿼리를 식별할 수 있습니다.

프로파일링 레벨

프로파일링 레벨은 기록되는 작업을 결정합니다:

  • 0 (끄기): 어떤 작업도 프로파일링되지 않습니다.
  • 1 (느린 작업): 구성된 임계값(slowms)보다 느린 작업만 프로파일링됩니다.
  • 2 (모든 작업): 모든 작업이 프로파일링되며, 이는 상당한 쓰기 부하를 발생시키므로 문제 해결을 위해 잠시만 사용해야 합니다.

상태 확인

현재 프로파일링 레벨을 보려면:

db.getProfilingStatus()

레벨 설정 (예시)

느린 작업에 대해서만 프로파일링을 활성화하려면 (100밀리초를 초과하는 작업):

// slowms를 100밀리초로 설정 (기본값은 일반적으로 100)
db.setProfilingLevel(1, { slowms: 100 })

팁: 필요한 정보를 수집한 후에는 항상 프로파일링 레벨을 0으로 되돌려 과도한 로깅으로 인한 성능 저하를 방지하십시오.

프로파일링된 느린 쿼리 보기

프로파일링된 작업은 모니터링 중인 특정 데이터베이스 내의 system.profile 컬렉션에 저장됩니다. 지난 1시간 동안 가장 느린 10개의 쿼리를 보려면:

db.system.profile.find().sort({millis: -1}).limit(10).pretty()

3. 리소스 사용률 메트릭

MongoDB가 CPU, 메모리 및 I/O 리소스를 어떻게 활용하는지 이해하는 것은 확장 결정에 필수적입니다.

메모리 및 스토리지 사용량: db.serverStatus()

serverStatus 내의 globalLockstorageEngine 섹션은 리소스 관리에 대한 깊은 통찰력을 제공합니다.

메모리 지표:

  • resident: 프로세스가 사용 중인 물리적 메모리 양입니다.
  • virtual: 프로세스에 할당된 총 가상 메모리입니다.
db.serverStatus().globalLock

잠금 경합 모니터링

MongoDB는 내부 잠금 메커니즘을 사용합니다. 잠금 획득 및 대기를 모니터링하면 동시성 병목 현상을 식별하는 데 도움이 됩니다.

globalLock의 주요 메트릭:

  • currentQueue.readers: 잠금을 기다리는 읽기 작업 수입니다.
  • currentQueue.writers: 잠금을 기다리는 쓰기 작업 수입니다.
  • totalTime: 모든 작업에서 잠금을 기다리는 데 소요된 총 시간입니다.

currentQueue의 값이 높으면 인덱스가 없거나 쓰기 작업이 지나치게 길어 읽기/쓰기 작업이 대기열에 쌓이고 있음을 나타내는 경우가 많습니다.

4. 인덱스 사용 및 상태: db.collection.stats()

제대로 활용되지 않거나 누락된 인덱스는 성능 저하의 가장 일반적인 원인입니다. stats() 명령어는 인덱스 효율성을 분석하는 데 도움이 됩니다.

특정 컬렉션(예: users)에서 실행할 때:

db.users.stats()

확인해야 할 주요 메트릭:

  • totalIndexSize: 해당 컬렉션의 모든 인덱스가 사용하는 총 디스크 공간입니다.
  • indexSizes: 인덱스별 공간 사용량 분석입니다.
  • 인덱스가 존재하지만 읽기에 사용되지 않는 경우, 제거를 고려해야 하는 오버헤드입니다.

5. 디스크 I/O 및 처리량: db.serverStatus() (네트워크 및 작업)

네트워크 활동과 작업 속도를 모니터링하면 데이터베이스 처리량을 파악할 수 있습니다.

작업 속도 (opcounters에서):

opcounters는 서버가 마지막으로 다시 시작된 이후 실행된 총 작업 수를 유형별로 추적합니다:

  • insert, query, update, delete, getmore, command.

시간이 지남에 따라 이러한 카운터의 변화를 추적하면(예: 두 번의 연속 serverStatus 호출 비교) 운영 처리량(초당 작업 수)을 계산할 수 있습니다.

예시 비교:

  1. T1 시간에 db.serverStatus().opcounters를 실행합니다.
  2. T2 시간에 db.serverStatus().opcounters를 실행합니다.
  3. T1 값을 T2 값에서 빼서 해당 간격 동안 실행된 총 작업 수를 구합니다.

사전 예방적 모니터링을 위한 모범 사례

  • 자동화가 핵심입니다: 수동 셸 명령어에만 의존하는 것은 비효율적입니다. MongoDB Cloud Manager/Ops Manager 또는 이러한 엔드포인트를 자동으로 쿼리하는 타사 모니터링 솔루션과 같은 도구를 사용하여 모니터링을 통합하십시오.
  • 기준선 설정: 시스템이 정상일 때 명령어를 실행하여 성능 기준선을 설정하십시오. 이 기준선에서 벗어나면 즉시 조사해야 합니다.
  • 지연 시간에 집중: 작업 수는 유용하지만, 최종 사용자 경험 문제를 진단할 때는 원시 처리량보다 지연 시간 메트릭(프로파일링 로그에서 보고된 시간 등)을 우선시하십시오.
  • 연결을 자주 확인하십시오: 트래픽이 많은 애플리케이션에서는 연결 제한이 가장 먼저 도달하는 경우가 많습니다. 구성된 최대값과 관련하여 db.serverStatus().connections.current를 모니터링하십시오.

실용적인 첫 번째 점검 목록

누군가 "MongoDB가 느리다"고 말하면 인덱스 변경으로 바로 뛰어들지 마십시오. 짧은 점검 목록으로 시작하고 본 내용을 기록하십시오.

서버가 활성 작업으로 과부하되었는지 확인하십시오:

db.currentOp({
  active: true,
  secs_running: { $gt: 2 }
});

분석 작업의 경우 몇 가지 장기 실행 작업이 정상일 수 있습니다. 많은 쓰기, 컬렉션 스캔 또는 차단된 작업이 쌓이는 것은 다릅니다. ns의 네임스페이스, op의 작업 유형, 쿼리 형태를 확인하십시오. 많은 작업이 하나의 업데이트나 인덱스 빌드 뒤에서 대기 중이라면, 해결 방법은 읽기 쿼리에 대한 누락된 인덱스와 동일하지 않습니다.

그런 다음 연결을 확인하십시오:

db.serverStatus().connections;

current가 빠르게 증가하면 애플리케이션 연결 풀이 잘못 구성되었거나, 배포로 인해 너무 많은 작업자가 생성되었거나, 클라이언트가 시간 초과되어 다시 연결되고 있음을 의미할 수 있습니다. available이 0에 가까우면 새 클라이언트가 연결에 실패할 수 있으므로 긴급 신호입니다. 올바른 해결책은 서버 제한을 높이는 것이 아니라 앱에서 풀 튜닝을 하는 것일 수 있습니다.

다음으로, 짧은 간격을 두고 작업 카운터를 두 번 확인하십시오:

const a = db.serverStatus().opcounters;
sleep(5000);
const b = db.serverStatus().opcounters;
printjson({
  insertPer5s: b.insert - a.insert,
  queryPer5s: b.query - a.query,
  updatePer5s: b.update - a.update,
  deletePer5s: b.delete - a.delete,
  commandPer5s: b.command - a.command
});

시작 이후의 카운터는 장기적인 맥락에 유용하지만, 알려진 간격에 대한 차이는 지금 무슨 일이 일어나고 있는지 알려줍니다. 명령 트래픽은 높지만 쿼리가 낮다면, 일반적인 읽기보다는 메타데이터 확인, 모니터링 노이즈 또는 드라이버 동작을 보고 있을 수 있습니다.

하드웨어를 비난하기 전에 explain() 사용

프로파일 컬렉션은 어떤 작업이 느린지 알려줄 수 있습니다. explain()은 CPU나 메모리를 추가하기 전에 쿼리가 느린 이유를 이해하는 데 도움이 됩니다.

db.users.find({ email: "[email protected]" }).explain("executionStats");

출력에서 totalDocsExaminednReturned와 비교하십시오. MongoDB가 한 명의 사용자를 반환하기 위해 엄청난 수의 문서를 검사한다면, 쿼리에 더 나은 인덱스나 다른 필터가 필요할 가능성이 높습니다. totalKeysExamined가 높다면 인덱스가 존재하지만 쿼리 패턴에 대해 충분히 선택적이지 않을 수 있습니다.

복합 쿼리의 경우 인덱스 순서가 중요합니다:

db.orders.find({
  accountId: "acct_123",
  status: "open",
  createdAt: { $gte: ISODate("2025-11-01T00:00:00Z") }
}).sort({ createdAt: -1 });

유용한 인덱스는 다음과 같을 수 있습니다:

db.orders.createIndex({ accountId: 1, status: 1, createdAt: -1 });

이는 보편적인 규칙이 아닙니다. 최상의 인덱스는 카디널리티, 정렬 순서 및 컬렉션에 도달하는 전체 쿼리 세트에 따라 달라집니다. 요점은 추측하는 대신 데이터베이스가 실행 계획을 보여주도록 하는 것입니다.

과도하게 반응하지 않고 프로파일링 데이터 읽기

프로파일링 레벨 2는 모든 작업을 기록하며 사용량이 많은 시스템에 오버헤드를 추가할 수 있습니다. 짧고 목표가 명확한 시간에만 사용하십시오. 합리적인 slowms 임계값이 있는 레벨 1은 느린 작업을 찾는 데 더 안전합니다.

db.setProfilingLevel(1, { slowms: 200 });

데이터를 수집한 후 가장 느린 항목을 검사하십시오:

db.system.profile.find(
  {},
  {
    ns: 1,
    op: 1,
    millis: 1,
    command: 1,
    keysExamined: 1,
    docsExamined: 1,
    nreturned: 1
  }
).sort({ millis: -1 }).limit(20).pretty();

느린 쿼리 하나가 항상 프로덕션 인시던트를 의미하는 것은 아닙니다. 예약된 보고서, 재시작 후 콜드 캐시 또는 드문 유지 관리 작업이 상위에 나타날 수 있습니다. 단일 샘플보다는 패턴이 더 중요합니다. 동일한 쿼리 형태가 반복적으로 나타나고 반환하는 것보다 훨씬 더 많은 문서를 검사한다면 실제 튜닝 대상이 있는 것입니다.

복제 세트 및 스토리지 압력 모니터링

복제 세트의 경우 성능은 프라이머리에만 관한 것이 아닙니다. 뒤처지는 세컨더리는 장애 조치 신뢰도와 클라이언트가 세컨더리 읽기를 사용하는 경우 읽기 워크로드에 영향을 미칠 수 있습니다.

rs.status();

비정상적인 멤버, 예상치 못한 상태 변경 또는 복구되지 않는 복제 지연이 있는지 확인하십시오. 정확한 허용 가능한 지연은 애플리케이션에 따라 다릅니다. 대기열과 같은 워크로드는 약간의 지연을 허용할 수 있습니다. 거의 실시간 읽기를 약속하는 대시보드는 그렇지 않을 수 있습니다.

스토리지 압력에도 동일한 맥락이 필요합니다. db.serverStatus()는 스토리지 엔진 및 WiredTiger 메트릭을 표시할 수 있지만, 디스크 수준 도구도 여전히 중요합니다. MongoDB가 느린 디스크를 기다리고 있다면 데이터베이스 내부의 셸 명령어는 근본 원인이 아닌 증상을 보여줍니다. 디스크 지연 시간, 파일 시스템 사용량, CPU 스틸, 메모리 압력과 같은 호스트 메트릭과 상호 연관시키십시오.

수동 확인을 알림으로 전환

수동 명령어는 조사 중에 가장 좋습니다. 정상 운영 중에는 유용한 신호를 자동화된 검사로 변환하십시오: 연결 사용량, 복제 상태, 느린 쿼리 비율, 디스크 사용량, 페이지 폴트 또는 캐시 압력(가능한 경우), 작업 지연 시간. 모든 1분 스파이크가 아닌 지속적인 잘못된 동작에 대해 알리십시오.

좋은 알림에는 맥락이 포함됩니다. "MongoDB 느린 쿼리 많음"은 데이터베이스, 컬렉션, 쿼리 형태, 현재 비율 및 최근 프로파일 샘플 또는 대시보드 패널에 대한 링크를 포함하는 알림보다 덜 유용합니다. 목표는 인시던트의 첫 10분을 단축하는 것입니다.

속도 저하 중 하지 말아야 할 일

한 번에 여러 가지 변경을 하지 마십시오. 동일한 인시던트에서 인덱스 추가, 연결 제한 증가, 애플리케이션 재시작, 풀 크기 변경은 증상을 제거할 수 있지만 어떤 조치가 도움이 되었는지 전혀 알 수 없게 만듭니다. 한 번에 하나의 변경을 하고, 개선되어야 하는 메트릭을 관찰하고, 기록을 유지하십시오.

killOp에 주의하십시오. 하나의 작업이 명백히 해로울 때 유용할 수 있지만, 임의의 장기 실행 작업을 종료하면 애플리케이션 동작이 더 나빠질 수 있습니다. 해당 작업이 마이그레이션, 백업, 인덱스 빌드 또는 보고 작업에 속하는 경우, 데이터베이스가 이미 심각한 문제에 빠지지 않는 한 중지하기 전에 소유자를 식별하십시오.

serverStatus()를 단일 마법 상태 점수로 취급하지 마십시오. 이는 카운터와 스냅샷의 모음입니다. 높은 값은 크고 사용량이 많은 시스템에서 정상일 수 있고, 낮은 값은 작고 지연 시간에 민감한 시스템에서 나쁠 수 있습니다. 유용한 질문은 값이 사용자 직면 문제와 일치하는 방식으로 변경되었는지 여부입니다.

또한 데이터베이스 증상을 배포 증상과 분리하십시오. 쿼리 형태를 변경하거나, 더 큰 연결 풀을 열거나, 백그라운드 마이그레이션을 시작하는 새로운 릴리스는 MongoDB가 근본 원인처럼 보이게 만들 수 있습니다. 데이터베이스 전용 수정을 하기 전에 느린 작업의 시기를 배포, 작업 일정, 백업 및 트래픽 변경과 비교하십시오.

MongoDB 모니터링은 현재 동작을 알려진 기준선과 비교할 때 가장 잘 작동합니다. db.currentOp(), db.serverStatus(), 프로파일링, explain() 및 복제 세트 검사는 문제가 쿼리, 인덱스, 클라이언트 연결 동작, 복제 또는 데이터베이스 아래의 호스트인지 결정하기에 충분한 증거를 제공합니다.