⚡ MongoDB 성능 튜닝 실전편: 인덱스 전략과 Aggregation Pipeline 최적화


MongoDB가 느려지는 원인의 90%는 잘못된 인덱스 구성비효율적인 Aggregation Pipeline 때문입니다.
이번 포스트에서는 쿼리 성능을 극적으로 올릴 수 있는 핵심 튜닝 전략을 실전 중심으로 소개합니다.


1️⃣ 인덱스(Index) 전략

✅ 기본 원칙

  • 항상 조회 조건(where)에 사용되는 필드는 인덱싱
  • 정렬(sort)에 자주 쓰이는 필드도 인덱스 고려
  • 복합 인덱스는 조건 사용 빈도 + 필드 순서가 중요

📌 단일 vs 복합 인덱스

// 단일 인덱스
db.users.createIndex({ age: 1 })

// 복합 인덱스 (조건 + 정렬)
db.users.createIndex({ age: 1, createdAt: -1 })

🧠 인덱스가 작동하지 않는 경우

  • 조건과 인덱스 순서가 다를 때
  • 복잡한 $or / $regex 조합
  • Aggregation에서 $project를 먼저 쓰는 경우

🔍 실행 계획 확인

db.users.find({ age: 30 }).explain("executionStats")
  • COLLSCAN: 전체 Collection 스캔 → 성능 저하
  • IXSCAN: 인덱스 사용 → OK

2️⃣ 인덱스 튜닝 실전 예제

🧪 문제 상황

db.orders.find({
  status: "shipped",
  createdAt: { $gte: ISODate("2023-01-01") }
}).sort({ createdAt: -1 })

이 쿼리가 느리다면?

✅ 해결 전략

  • 복합 인덱스 생성
db.orders.createIndex({ status: 1, createdAt: -1 })
📌 WHERE → SORT 순서대로 인덱스 필드 배치하는 게 핵심입니다.

 


3️⃣ Aggregation Pipeline 최적화

Aggregation은 강력하지만, 성능 병목도 자주 발생하는 영역입니다.

⚠️ 자주 발생하는 비효율 구조

db.sales.aggregate([
  { $project: { total: { $multiply: ["$price", "$qty"] } } },
  { $match: { total: { $gt: 100 } } }
])

$project → $match 순서는 인덱스를 사용할 수 없습니다.

✅ 올바른 순서

db.sales.aggregate([
  { $match: { price: { $gt: 10 } } },
  { $project: { total: { $multiply: ["$price", "$qty"] } } }
])
  • 인덱스 가능한 조건은 $match로 먼저 걸어야 성능 확보 가능
  • 필터 → 가공 → 정렬 → 그룹 순서로 설계

4️⃣ $group 최적화 팁

대규모 데이터 집계에서 $group은 가장 느린 연산입니다.

💡 최적화 전략

  • group 전에 가능한 한 match/filter로 줄이기
  • allowDiskUse: true 옵션으로 메모리 초과 방지
db.logs.aggregate([
  { $match: { level: "ERROR" } },
  { $group: { _id: "$service", count: { $sum: 1 } } }
], { allowDiskUse: true })

 


 

5️⃣ 성능 튜닝 종합 체크리스트

  • ✅ 모든 쿼리는 .explain() 으로 실행계획 확인
  • ✅ 인덱스는 조회 조건 + 정렬 순서 기반으로 설계
  • ✅ Aggregation은 $match → $project → $sort → $group 순서 지키기
  • 복합 인덱스는 자주 사용하는 쿼리 패턴 중심으로

📌 추천 도구

  • MongoDB Compass → 쿼리 분석 + 인덱스 추천
  • Atlas Performance Advisor → 인덱스 자동 추천
  • mongostat / mongotop → 실시간 부하 확인

✅ 마무리 요약

  • 인덱스 전략은 MongoDB 성능의 핵심!
  • Aggregation은 설계 순서와 필터링 범위가 성능을 좌우
  • 쿼리 튜닝은 분석(Explain) → 테스트 → 튜닝의 반복

도움 되셨다면 공감/구독도 부탁드립니다 😊

🔥 MongoDB 운영 중 자주 발생하는 문제와 실전 대응법

MongoDB는 유연하고 강력한 NoSQL DB지만, 실 운영 환경에서는 다양한 이슈가 발생할 수 있습니다.
이 포스팅에서는 현장에서 자주 접하는 문제 상황과 그에 대한 대응 전략을 실제 경험 기반으로 정리해드립니다.


1️⃣ Oplog Overflow (오플로그 오버플로우)

  • 🔍 문제 현상: Secondary 노드 복제 실패, 에러 로그에 missing oplog records
  • 🧠 원인: Secondary가 복제 지연 상태일 때 Oplog에서 오래된 로그가 삭제됨

✅ 대응 방법

  • Replication 지연 확인: rs.printSlaveReplicationInfo()
  • Secondary 초기화 후 재시작 (전체 동기화)

🛡️ 예방 전략

  • Oplog 크기 충분히 확보 (디폴트보다 ↑)
  • 복제 지연 감시 → 알람 설정

2️⃣ Replication Lag (복제 지연)

  • 🔍 문제 현상: Secondary가 Primary 변경을 늦게 반영함
  • 🧠 원인: 느린 네트워크, Disk I/O 병목, 쿼리 부하

✅ 대응 방법

rs.printSlaveReplicationInfo()
  • 지연이 10초 이상이면 주의, 수 분이면 위험
  • Secondary가 과부하 상태인지 top 또는 mongotop으로 확인

🛡️ 예방 전략

  • 네트워크 품질 확인 (Private VPC 권장)
  • Secondary에 백업, 분석 작업 몰리지 않도록 분리

3️⃣ Lock 경합 (Write/Read 경합)

  • 🔍 문제 현상: 응답 지연, CPU 사용량 급증
  • 🧠 원인: 동일 컬렉션에 대한 동시 쓰기, 대용량 업데이트

✅ 대응 방법

  • db.currentOp()로 오래된 쿼리 확인
  • killOp(opid)로 불필요한 작업 중단

🛡️ 예방 전략

  • 배치 작업은 슬라이스로 나눠서 실행
  • 업데이트 쿼리는 조건 최소화, 필드 선택적으로 업데이트

4️⃣ 커넥션 수 초과 (Too Many Connections)

  • 🔍 문제 현상: "Too many connections" 에러
  • 🧠 원인: 커넥션 풀 미사용, 앱 재시도 로직 과잉

✅ 대응 방법

db.serverStatus().connections
  • 앱의 커넥션 풀 설정 확인 (e.g., Mongoose, Spring Data 등)
  • 최대 커넥션 수 조정: maxIncomingConnections 파라미터

🛡️ 예방 전략

  • 앱단에서 커넥션 풀 필수
  • Atlas에서는 Auto-scaling + 경고 알림 활용

5️⃣ 쿼리 성능 저하 (Index 미사용 / Full Collection Scan)

  • 🔍 문제 현상: 응답 시간 ↑, 서버 CPU 사용량 ↑
  • 🧠 원인: 인덱스 누락, 복합 조건 쿼리의 비효율적 설계

✅ 대응 방법

db.collection.find({ ... }).explain("executionStats")
  • COLLSCAN이면 인덱스 미사용
  • 적절한 인덱스 생성: db.collection.createIndex()

🛡️ 예방 전략

  • slow query log 활성화 & 주기적 리뷰
  • 복합 인덱스는 자주 쓰는 순서로 구성

6️⃣ 디스크 공간 부족

  • 🔍 문제 현상: MongoDB 다운, insert 불가
  • 🧠 원인: oplog, log, WiredTiger cache가 디스크를 가득 채움

✅ 대응 방법

  • db.stats()로 DB 사이즈 확인
  • logRotate 설정으로 로그 압축

🛡️ 예방 전략

  • 모니터링 도구에서 디스크 임계값 경고 설정
  • 데이터 아카이빙 전략 운영 (cold data 이동)

✅ 마무리 요약

  • Oplog & Replication Lag은 고가용성 핵심
  • Lock 경합, 쿼리 성능 저하는 실시간 트래픽 대응력에 영향
  • 모니터링 도구 & 경고 알람은 반드시 설정해야 함
  • 지속적인 점검 & 튜닝 없이는 MongoDB도 결국 느려집니다

도움이 되셨다면 공감과 구독도 부탁드립니다! 😊

🛠️ MongoDB DBA가 꼭 챙겨야 할 관리 포인트 정리


MongoDB를 운영 환경에 도입했다면, 이제부터는 DBA 입장에서의 관리 전략이 중요합니다.
이 글에서는 MongoDB를 안정적으로 운영하기 위한 주요 관리 포인트를 하나씩 짚어볼게요.


1️⃣ 운영 환경 구성

✅ Replica Set

  • MongoDB 고가용성을 위한 기본 구성
  • 최소 3개 이상의 노드 구성: 1 Primary + 2 Secondary
  • 자동 Failover 지원
rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "mongo1:27017" },
    { _id: 1, host: "mongo2:27017" },
    { _id: 2, host: "mongo3:27017" }
  ]
});

✅ Sharding

  • 수평 확장(Scale-out)을 위한 기능
  • 샤드 키(Shard Key) 선정이 매우 중요
  • 불균형 파티셔닝은 성능 저하로 이어짐
📌 샤딩은 반드시 트래픽 패턴 기반으로 설계해야 합니다.

2️⃣ 모니터링 포인트

운영 환경에서는 지속적인 모니터링이 필수입니다.

🧪 주요 명령어

  • mongostat: 실시간 트래픽 확인
  • mongotop: 컬렉션별 읽기/쓰기 시간 확인

📊 MongoDB Atlas Monitoring

  • CPU, 메모리, IOPS, 컬렉션별 쿼리 수행 시간
  • Slow Query Analytics 제공
🔍 300ms 이상 걸리는 쿼리는 slow query log로 추적하세요.

3️⃣ 성능 최적화

✅ 인덱스 관리

  • 쿼리에 사용되는 필드는 인덱스 필수
  • 복합 인덱스는 순서 중요 ({ a: 1, b: 1 })

✅ 쿼리 분석

db.collection.find({ a: 1 }).explain("executionStats")
  • COLLSCAN → 인덱스 미사용 경고
  • IXSCAN → 인덱스 정상 사용

✅ Write Concern / Read Concern

  • 데이터 일관성을 위해 적절히 조정 필요
  • 기본: { w: "majority" }

4️⃣ 백업과 복구

🧰 mongodump & mongorestore

# 백업
mongodump --out=/data/backup

# 복구
mongorestore /data/backup

🌀 Point-in-Time 복구 (MongoDB Atlas)

  • 클러스터 설정 시 PITR(지점 복구) 설정 가능
  • 실수로 인한 삭제/변경 복구에 유용

5️⃣ 사용자 권한과 보안

🔐 Role-Based Access Control (RBAC)

  • read, readWrite, dbAdmin 등 역할 기반 권한
  • 운영 계정에는 최소 권한 부여 원칙 적용

🔒 인증 & 암호화

  • 비밀번호 인증 기본
  • LDAP, Kerberos 등 외부 인증 연동 가능
  • 데이터 암호화: TLS/SSL + Encryption-at-Rest

6️⃣ 장애 대응 체크리스트

  • Primary 전환 감지 (rs.status())
  • Secondary 복제 지연 확인 (Replication Lag)
  • 디스크 공간, 커넥션 수 상시 모니터링
  • 애플리케이션에서 재시도 로직 포함 여부 확인
📍 MongoDB는 장애 발생 시 자동 복구 기능이 있지만, DBA의 사전 대응 설계가 핵심입니다.

✅ 마무리 요약

  • Replica Set은 기본, Sharding은 상황에 따라
  • 인덱스/쿼리 분석은 성능 유지의 핵심
  • 권한 관리와 백업은 운영 안정성의 필수 요소
  • Atlas 활용 시 시각화된 모니터링과 자동 백업이 장점

 

도움이 되셨다면 공감과 구독도 부탁드립니다! 😊

✅ 현재 Oplog 사이즈 확인 방법

Mongo Shell에서 확인

db.getReplicationInfo()

📘 예시 출력:

{
  logSizeMB: 1024,
  usedMB: 23.45,
  timeDiff: 14400,
  timeDiffHours: 4,
  tFirst: ISODate("2025-04-18T03:00:00Z"),
  tLast: ISODate("2025-04-18T07:00:00Z"),
  now: ISODate("2025-04-18T07:00:01Z")
}
logSizeMB 전체 Oplog 크기 (MB)
usedMB 현재 사용 중인 용량 (MB)
timeDiff Oplog에 저장된 첫 번째 ~ 마지막 기록 사이의 시간 간격 (초)
timeDiffHours 위 시간의 시간 단위 버전
tFirst Oplog에서 가장 오래된 기록의 시간
tLast 가장 최근의 Oplog 기록 시간

컬렉션 직접 확인 (local.oplog.rs)

use local db.oplog.rs.stats().maxSize / 1024 / 124 // MB 단위로 변환

Oplog 컬렉션 자체의 maxSize 확인 가능합니다.

 

 

🛠️ 수동으로 oplog 크기 변경하는 법

MongoDB는 실행 중에는 oplog 크기를 직접 변경할 수 없어요. 하지만 아래와 같이 수동으로 변경은 가능합니다:

  1. 복제 중단 (노드 내리기)
  2. 기존 oplog.rs 삭제
  3. 새 크기로 다시 초기화
# Step 1: mongod 종료
sudo systemctl stop mongod

# Step 2: oplog 크기 변경하여 다시 시작
mongod --replSet rs0 --oplogSize 2048

⚠️ 주의: 이 작업은 데이터 손실 위험이 있으므로,  Secondary 노드에서만 수행합니다.

🚀 MongoDB 기본기, RDBMS와 비교하며 쉽게 이해하기


안녕하세요! 이 글에서는 MongoDB의 기본 개념RDBMS(MySQL 등)와 비교하면서 쉽게 정리해볼게요.
SQL은 익숙한데 NoSQL이 처음이라면, 아래 비교와 실습 예제만 따라와도 MongoDB 감이 팍 잡힐 거예요!


✅ MongoDB란?

MongoDB는 NoSQL 기반의 문서형 데이터베이스입니다.
데이터를 테이블이 아닌 JSON 형태의 Document로 저장하고, 유연한 스키마를 지원해요.

📌 NoSQL = Not Only SQL
기존 SQL 방식 외에도 다양한 구조의 데이터를 저장할 수 있는 DB
  • 스키마가 자유로워서 빠른 개발에 적합
  • JSON 형태의 데이터 저장 (BSON으로 처리)
  • 수평 확장(Sharding)에 강해 대규모 데이터 처리 가능

1️⃣ RDBMS vs MongoDB 구조 비교

RDBMS (MySQL 등) MongoDB
Database Database
Table Collection
Row Document (JSON 형태)
Column Field
SQL Mongo Query Language (JavaScript 기반)

핵심 차이: MongoDB는 스키마 정의 없이, 자유롭게 JSON 형태로 데이터를 저장합니다.


2️⃣ 설치 & 실행 (로컬)

MongoDB 공식 다운로드에서 Community 버전 설치

# MongoDB 서버 실행
mongod

# Mongo Shell 접속
mongosh
최근에는 mongosh가 기본 쉘입니다 (기존 mongo 명령어에서 변경됨).

3️⃣ MongoDB 기본 명령어

# 데이터베이스 목록
show dbs

# DB 선택/생성
use myDB

# 컬렉션에 도큐먼트 삽입
db.users.insertOne({ name: "Alice", age: 25 })

# 컬렉션 조회
show collections

# 도큐먼트 전체 조회
db.users.find()

# 조건 검색 (나이 ≥ 20)
db.users.find({ age: { $gte: 20 } })

4️⃣ 데이터 삽입 비교

🟦 RDBMS

CREATE TABLE users (
  id INT AUTO_INCREMENT,
  name VARCHAR(50),
  age INT
);

INSERT INTO users (name, age) VALUES ('Alice', 25);

🟩 MongoDB

use myDB;
db.users.insertOne({ name: "Alice", age: 25 });

MongoDB는 컬렉션이 없어도 자동 생성되고, 스키마 없이 바로 삽입 가능해요.


5️⃣ 데이터 조회 비교

🟦 MySQL

SELECT * FROM users WHERE age >= 20;

🟩 MongoDB

db.users.find({ age: { $gte: 20 } });

조건문은 SQL과 달리 $gte, $lte, $in 같은 연산자를 사용합니다.


6️⃣ 유연한 스키마란?

MongoDB에서는 컬렉션 내 도큐먼트 구조가 서로 달라도 저장이 가능합니다.

// 첫 도큐먼트
{ name: "Tom", age: 30 }

// 두 번째 도큐먼트
{ name: "Jane", email: "jane@example.com" }

→ 초반 개발 속도가 빠르고, 구조가 자주 바뀌는 프로젝트에 적합해요.


7️⃣ 기타 기능 비교 요약

기능 RDBMS MongoDB
트랜잭션 강력 (ACID 보장) 다중 도큐먼트 트랜잭션 지원 (최신 버전)
스키마 엄격 (정의 필수) 자유로움 (필요 시 유효성 검사 가능)
조인 JOIN으로 자유롭게 가능 JOIN은 제한적 (Aggregation 사용)
확장성 수직 확장 (서버 성능 ↑) 수평 확장 (Sharding 지원)

✨ 마무리 요약

  • MongoDB는 테이블 대신 컬렉션, 행(Row) 대신 도큐먼트를 사용
  • JSON 스타일 문법으로 간편한 삽입/조회 가능
  • 스키마가 자유로워 빠르게 시작할 수 있음
  • 대용량 데이터에 강하고, 클라우드 연동이 쉬움

 

도움이 되셨다면 공감과 구독도 부탁드립니다!😀

+ Recent posts