20170321

MongoDB 3.4 aggregation 파이프라인 튜닝

리포트 쿼리 하나가 aggregation 안에서 7초 걸려서 튜닝한 기록. MongoDB 3.4 기준.

컬렉션: events, 5천만 doc. 하루치 집계해서 이벤트 타입별 count를 내는 쿼리.

db.events.aggregate([
  { $match: { createdAt: { $gte: ISODate("2017-03-20"), $lt: ISODate("2017-03-21") } } },
  { $group: { _id: "$type", count: { $sum: 1 } } },
  { $sort: { count: -1 } }
]);
// 7.2 초

Explain 보니 $match가 인덱스 안 탐. 인덱스는 {createdAt: 1, userId: 1} 이었는데 쿼리에서 userId 없어서 prefix만 탄다고 하더라. 실제로는 index만 스캔해도 빠를텐데 옵티마이저가 전체 읽고 있었음.

조치:

1. {createdAt: 1, type: 1} 인덱스 추가. type이 group key라 covering 가능.

2. 3.4부터 추가된 $project + $expr 기능 검토했는데 이 케이스엔 불필요. 그냥 인덱스로 해결.

3. $match를 맨 앞에 두는건 기본중 기본. 원 쿼리도 맞음.

db.events.aggregate([
  { $match: { createdAt: { $gte: ISODate("2017-03-20"), $lt: ISODate("2017-03-21") } } },
  { $group: { _id: "$type", count: { $sum: 1 } } },
  { $sort: { count: -1 } }
], { hint: { createdAt: 1, type: 1 } });
// 0.9 초

인덱스 커버 + hint 명시. 대략 8배 향상.

추가 팁:

  • allowDiskUse: true 옵션. 메모리 100MB 넘는 aggregation은 이거 없으면 실패. 리포트 쿼리엔 필수
  • 3.4의 $facet: 한 번 순회로 여러 집계 결과를 배열로 받음. 대시보드 같은데 유용
  • $lookup (join)은 쓸만하긴 한데 크면 역시 느림. denormalize가 Mongo 철학에 맞음

MongoDB는 인덱스 설계가 실무 성능의 90%. RDBMS랑 똑같이 쿼리 패턴 보고 인덱스 짜야 함. 문서 DB라고 "아무거나 넣으면 된다"가 아님. 당연하지만 매번 상기해야 되는 포인트.

댓글 없음: