Elasticsearch 대용량 집계 쿼리 최적화 경험

문제 상황

사용자 행동 로그를 Elasticsearch에 적재하고 있는데, 데이터가 1억 건을 넘어가면서 월간 집계 API 응답이 30초 이상 걸리기 시작했다. 프론트엔드 타임아웃(10초)에 걸려 대시보드가 제대로 표시되지 않았다.

기존 쿼리 구조

{
  "query": {
    "range": {
      "timestamp": {
        "gte": "2022-02-01",
        "lte": "2022-02-28"
      }
    }
  },
  "aggs": {
    "daily_users": {
      "date_histogram": {
        "field": "timestamp",
        "calendar_interval": "day"
      },
      "aggs": {
        "unique_users": {
          "cardinality": {
            "field": "user_id"
          }
        }
      }
    }
  }
}

문제는 user_id 필드가 text 타입이었고, cardinality 집계가 매번 전체 문서를 스캔했다.

해결 방법

1. 인덱스 매핑 수정

{
  "mappings": {
    "properties": {
      "user_id": {
        "type": "keyword"
      },
      "timestamp": {
        "type": "date"
      }
    }
  }
}

user_idkeyword 타입으로 변경하고 reindex를 진행했다.

2. 쿼리 크기 제한

{
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "timestamp": {
              "gte": "2022-02-01",
              "lte": "2022-02-28"
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "daily_users": {
      "date_histogram": {
        "field": "timestamp",
        "calendar_interval": "day"
      },
      "aggs": {
        "unique_users": {
          "cardinality": {
            "field": "user_id",
            "precision_threshold": 10000
          }
        }
      }
    }
  }
}
  • size: 0으로 문서 반환 제거
  • query 대신 filter 사용 (스코어링 불필요)
  • precision_threshold 설정으로 메모리 사용량 조절

3. 인덱스 샤드 재조정

기존 5개 샤드를 10개로 늘려 병렬 처리 성능을 개선했다. 각 샤드 크기가 30GB 이하로 유지되도록 조정했다.

결과

  • 응답 시간: 30초 → 2.8초
  • CPU 사용률: 85% → 40%
  • 메모리 사용량: 12GB → 6GB

집계 쿼리는 필드 타입과 인덱스 구조가 성능에 결정적인 영향을 준다는 것을 다시 확인했다.