Elasticsearch 대용량 데이터 집계 쿼리 최적화
문제 상황
사용자 행동 로그를 Elasticsearch에 저장하고 있는데, 최근 3개월치 데이터(약 5억 건)에서 일별/카테고리별 집계를 뽑는 쿼리가 30초 이상 걸리며 종종 타임아웃되었다.
기존에는 단순 terms aggregation을 중첩해서 사용했다.
{
"aggs": {
"by_date": {
"terms": {
"field": "date",
"size": 90
},
"aggs": {
"by_category": {
"terms": {
"field": "category",
"size": 100
}
}
}
}
}
}
원인 분석
termsaggregation은 모든 샤드에서 결과를 수집 후 정렬하므로 메모리 부담이 크다date필드가 keyword 타입이라 불필요한 오버헤드 발생- 시간 범위 필터 없이 전체 데이터 스캔
해결 방법
1. Date Histogram 사용
날짜별 집계는 date_histogram을 사용하도록 변경했다.
{
"query": {
"range": {
"timestamp": {
"gte": "now-90d/d"
}
}
},
"aggs": {
"by_date": {
"date_histogram": {
"field": "timestamp",
"calendar_interval": "day"
},
"aggs": {
"by_category": {
"terms": {
"field": "category"
}
}
}
}
}
}
2. 인덱스 매핑 수정
날짜 필드를 date 타입으로 변경하고, 자주 집계하는 필드에 eager_global_ordinals 설정을 추가했다.
{
"mappings": {
"properties": {
"timestamp": {
"type": "date"
},
"category": {
"type": "keyword",
"eager_global_ordinals": true
}
}
}
}
3. Composite Aggregation 활용
페이지네이션이 필요한 경우 composite aggregation을 사용했다.
{
"aggs": {
"my_buckets": {
"composite": {
"size": 100,
"sources": [
{"date": {"date_histogram": {"field": "timestamp", "calendar_interval": "day"}}},
{"category": {"terms": {"field": "category"}}}
]
}
}
}
}
결과
- 쿼리 응답 시간: 30초 → 2.8초
- 타임아웃 에러 제로
- 힙 메모리 사용량 40% 감소
인덱스 리인덱싱이 필요해서 배포는 주말 새벽에 진행했다. 앞으로는 집계가 빈번한 필드는 처음부터 타입과 설정을 신경 써야겠다.