pile·
DB / 데이터·SSG.COMSSG.COM·

이상적인 구조가 빠른 성능은 아닙니다

SSG.COM에서 MongoDB 컬렉션 구조를 개편하면서 겪은 성능 문제와 해결 과정을 공유한 글이다. '이상적인 구조가 반드시 빠른 성능은 아니다'는 교훈을 중심으로, 구조 개편 후 오히려 발생한 커넥션 풀 압박과 getMore 쿼리 반복 문제를 추적하고 해결한 실전 사례다.

핵심 포인트
  • 기존 구조 문제: `themeList[].itemList[]` 중첩 배열 → 테마 수만큼 중복 쿼리, 비효율적 데이터 접근
  • 개편 방향: "1 document = 1 product" 플랫 구조 → 중복 쿼리 제거, 필요한 상품만 조회
  • 개편 후 신규 문제 1: 병렬 API 호출로 MongoDB 커넥션 풀 압박 발생
  • 개편 후 신규 문제 2: MongoDB 기본 배치 크기 초과 시 getMore 쿼리 반복 발생
  • 핵심 해결책: `limit`와 `cursorBatchSize`를 동일값으로 설정 → getMore 완전 제거
  • 핵심 교훈: "구조 분리는 목표가 아니라 수단" — 성능 측정 없는 구조 개편은 새 문제를 낳는다
상세 정리
  • 원인 분석: 기존 중첩 구조에서 테마 3개 = 쿼리 3회, 같은 상품이 여러 테마에 중복 포함
  • 개편 후 구조: 상품 컬렉션 정규화, 테마-상품 관계를 별도 컬렉션으로 분리
  • 중복 쿼리 제거 확인: explain() 로 쿼리 횟수 감소 검증
  • 커넥션 풀 압박 원인: 플랫 구조 전환 후 병렬 API 요청 증가 → 동시 커넥션 수 급증
  • 커넥션 풀 해결: maxPoolSize 조정 + 요청 직렬화(병렬 → 순차) 부분 적용
  • getMore 발생 원인: MongoDB cursor 기본 배치 크기(101개 또는 16MB) 초과 시 자동 추가 쿼리 발생
  • getMore 문제 추적: MongoDB Atlas 모니터링에서 getMore 쿼리 다수 관찰
  • 해결 코드: `collection.find(query).limit(N).batchSize(N)` — limit = batchSize로 단일 왕복 보장
  • 성능 측정 결과: getMore 제거 후 API 응답시간 개선 확인
  • 일반화 교훈: 페이지네이션 없이 전체 조회 시 항상 limit+batchSize 쌍으로 설정 권고
왜 읽나MongoDB를 사용하는 팀이라면 getMore 쿼리가 성능 병목이 될 수 있다는 사실과, limit과 cursorBatchSize를 맞추는 단순한 설정으로 이를 해결할 수 있다는 점을 실전 사례로 확인할 수 있다.
SSG.COM
SSG.COM 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. DB / 데이터·여기어때 (GC컴퍼니)여기어때 (GC컴퍼니)·

    분석가가 직접 만든 마케팅 자동화 시스템 — 오디언스 빌더 제작기

    여기어때 데이터 분석팀(UB팀)이 개발 경험 없이 AI 에이전트 바이브 코딩으로 마케팅 자동화 시스템 '오디언스 빌더'를 구축한 과정이다. SQL 없이 UI에서 타겟 조건을 설정하면 BigQuery에서 수천만 행 대상자를 추출해 Kafka·S3를 통해 마케팅 도구로 전달하는 파이프라인을 직접 만들어, 캠페인 준비 시간을 2~3일에서 10분으로 단축했다.

    요약 이어보기
    #data-pipeline#kafka#postgres+2
  2. DB / 데이터·vercel-blogVercel Blog·

    모든 Vercel 빌드를 지탱하는 데이터베이스 마이그레이션 이야기

    Vercel이 빌드 웜 풀(warm pool) 상태 데이터를 Redis에서 DynamoDB로 마이그레이션한 과정을 다룬다. 빌드 풀의 청구 매핑 데이터가 Redis 장애 시 복구 불가능하다는 구조적 위험이 이주를 결정한 핵심 이유였고, 24/7 프로덕션 트래픽 속에서 다운타임 없이 5단계 점진적 전환을 완료했다.

    요약 이어보기
    #database-migration#zero-downtime#redis+2
  3. DB / 데이터·channel-talk채널톡·

    DynamoDB 핫 파티션을 해결하는 3가지 방법 (3): 조회를 인덱스 테이블로 옮기기

    채널톡이 DynamoDB 핫 파티션을 해결하려 GSI를 별도 인덱스 테이블로 떼어낸 연재의 마지막 편으로, 조회 경로를 GSI에서 인덱스 테이블로 옮긴 과정을 다룬다. 2편에서 쓰기 파이프라인이 완성돼 WriteThrottleEvents가 월 110만 회에서 0회가 됐지만, 조회 코드가 여전히 GSI를 읽고 있어 GSI를 지울 수 없었다. GSI가 읽기에서 대신해주던 키 해석·정확 일치·삭제 필터·페이지 채우기를 사내 공통 라이브러리의 조회 규칙으로 흡수해, 호출부는 GSI든 인덱스 테이블이든 같은 코드를 쓰게 만들었다.

    요약 이어보기
    #migration#dynamodb#hot-partition+5