Elasticsearch 집계 쿼리 성능 개선 - 캐싱 전략

문제 상황

재택근무가 시작되면서 대시보드 사용량이 급증했다. 특히 일별/주별 통계를 보여주는 페이지에서 응답이 5초 이상 걸리는 경우가 빈번했다. Kibana로 확인해보니 집계 쿼리가 대부분의 시간을 소모하고 있었다.

원인 분석

문제가 된 쿼리는 다음과 같았다.

{
  "aggs": {
    "daily_stats": {
      "date_histogram": {
        "field": "timestamp",
        "interval": "day"
      },
      "aggs": {
        "unique_users": { "cardinality": { "field": "user_id" } },
        "total_amount": { "sum": { "field": "amount" } }
      }
    }
  }
}

3개월치 데이터(약 500만 건)를 매번 집계하고 있었다. 더 큰 문제는 여러 사용자가 동시에 같은 쿼리를 날리고 있다는 점이었다.

해결 방법

1. 쿼리 최적화

먼저 불필요한 필드를 제거하고 _source를 false로 설정했다. 집계에만 필요한 필드만 로드하도록 수정했다.

2. Redis 캐싱

Node.js 서버에서 캐싱 레이어를 추가했다.

const redis = require('redis');
const client = redis.createClient();

async function getDailyStats(startDate, endDate) {
  const cacheKey = `stats:${startDate}:${endDate}`;
  
  const cached = await client.get(cacheKey);
  if (cached) {
    return JSON.parse(cached);
  }
  
  const result = await esClient.search({
    index: 'transactions',
    body: buildAggregationQuery(startDate, endDate)
  });
  
  await client.setex(cacheKey, 300, JSON.stringify(result));
  return result;
}

TTL은 5분으로 설정했다. 실시간성이 중요하지 않은 통계 데이터였기 때문에 충분했다.

3. 날짜 범위 제한

프론트에서 기본적으로 최근 30일만 조회하도록 변경했다. 더 긴 기간이 필요한 경우에만 명시적으로 선택하도록 했다.

결과

  • 평균 응답 시간: 5.2초 → 480ms
  • 캐시 히트율: 약 78%
  • ES 클러스터 부하 50% 감소

재택근무로 트래픽이 늘어나면서 기존에 묻혀있던 성능 이슈가 드러났다. 캐싱은 가장 기본적인 해결책이지만, 확실히 효과가 있었다.