테스트 데이터 준비
먼저 페이징 테스트에 사용할 샘플 데이터를 Bulk API로 입력합니다.
POST /_bulk
{ "create": { "_index": "us", "_type": "user", "_id": "1" }}
{ "email" : "john@smith.com", "name" : "John Smith", "username" : "@john" }
{ "create": { "_index": "us", "_type": "user", "_id": "2" }}
{ "email" : "mary@jones.com", "name" : "Mary Jones", "username" : "@mary" }
{ "create": { "_index": "us", "_type": "tweet", "_id": "3" }}
{ "date" : "2014-09-13", "name" : "Mary Jones", "tweet" : "Elasticsearch means full text search has never been so easy", "user_id" : 2 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "4" }}
{ "date" : "2014-09-14", "name" : "John Smith", "tweet" : "@mary it is not just text, it does everything", "user_id" : 1 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "5" }}
{ "date" : "2014-09-15", "name" : "Mary Jones", "tweet" : "However did I manage before Elasticsearch?", "user_id" : 2 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "6" }}
{ "date" : "2014-09-16", "name" : "John Smith", "tweet" : "The Elasticsearch API is really easy to use", "user_id" : 1 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "7" }}
{ "date" : "2014-09-17", "name" : "Mary Jones", "tweet" : "The Query DSL is really powerful and flexible", "user_id" : 2 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "8" }}
{ "date" : "2014-09-18", "name" : "John Smith", "user_id" : 1 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "9" }}
{ "date" : "2014-09-19", "name" : "Mary Jones", "tweet" : "Geo-location aggregations are really cool", "user_id" : 2 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "10" }}
{ "date" : "2014-09-20", "name" : "John Smith", "tweet" : "Elasticsearch surely is one of the hottest new NoSQL products", "user_id" : 1 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "11" }}
{ "date" : "2014-09-21", "name" : "Mary Jones", "tweet" : "Elasticsearch is built for the cloud, easy to scale", "user_id" : 2 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "12" }}
{ "date" : "2014-09-22", "name" : "John Smith", "tweet" : "Elasticsearch and I have left the honeymoon stage, and I still love her.", "user_id" : 1 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "13" }}
{ "date" : "2014-09-23", "name" : "Mary Jones", "tweet" : "So yes, I am an Elasticsearch fanboy", "user_id" : 2 }
{ "create": { "_index": "us", "_type": "tweet", "_id": "14" }}
{ "date" : "2014-09-24", "name" : "John Smith", "tweet" : "How many more cheesy tweets do I have to write?", "user_id" : 1 }
1. from + size 기반의 얕은 페이징
Elasticsearch에서 가장 직관적인 페이징 방식은 from과 size 파라미터를 사용하는 것입니다. 일반적인 검색 흐름은 다음과 같습니다.
- 클라이언트가 특정 노드에 요청을 보냄
- 해당 노드가 모든 샤드로 요청을 전달하고, 각 샤드는
from + size만큼의 데이터를 조회 - 코디네이팅 노드가 모든 결과를 취합하여 요청된 순서로 정렬한 후,
from위치부터size개수만큼 잘라서 반환
얕은 페이징은 내부적으로 from + size 개수만큼의 전체 문서를 먼저 가져온 후, 앞부분을 잘라내는 방식으로 동작합니다.
예제: 5번째 문서부터 10번째 문서까지 조회 (0부터 시작)
GET /us/_search
{
"from": 5,
"size": 5
}
from은 결과에서 건너뛸 문서의 수를, size는 반환할 문서의 수를 의미합니다. 위 예제는 0, 1, 2, 3, 4번 문서를 건너뛰고 5, 6, 7, 8, 9번 문서를 반환합니다.
장점: 데이터 양이 적을 때 매우 효율적이며 사용법이 간단합니다.
단점: from 값이 커질수록 성능이 급격히 저하됩니다. 이는 Elasticsearch가 모든 샤드에서 from + size 개의 문서를 가져와 힙 메모리에 로드한 후 정렬해야 하기 때문입니다. 대용량 데이터에서 깊은 페이지를 요청하면 메모리 부족(OOM)으로 클러스터가 중단될 위험이 있습니다.
2. Scroll API를 활용한 깊은 페이징
대량의 데이터를 순차적으로 처리해야 하거나, 깊은 페이지네이션이 필요할 때는 Scroll API가 적합합니다. Scroll은 쿼리가 실행된 시점의 인덱스 스냅샷을 유지하여, 이후 요청들은 이 스냅샷에서 데이터를 읽어옵니다. 즉, Scroll은 초기화와 순회의 두 단계로 이루어집니다.
2.1 초기화 (Scroll 초기화)
첫 번째 요청으로 스냅샷을 생성하고, 결과의 첫 번째 배치를 가져옵니다.
GET us/_search?scroll=3m
{
"query": { "match_all": {} },
"size": 3
}
위 요청은 us 인덱스의 모든 문서를 대상으로 3분(3m) 동안 유효한 스냅샷을 생성하고, 처음 3개의 문서를 반환합니다. 응답에는 _scroll_id가 포함됩니다.
2.2 순회 (Scroll 계속 요청)
반환된 _scroll_id와 함께 Scroll API를 호출하여 다음 배치의 데이터를 가져옵니다. 더 이상 반환할 문서가 없을 때까지 반복합니다.
GET /_search/scroll
{
"scroll": "1m",
"scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAADiVFmc0QlJqSzhnUUhXT3ZiQjl2c2h5N3cAAAAAAAA71RZyNFJxSU1vOFJZQ2VRcVBHLXJvb29nAAAAAAAAOJQWZzRCUmpLOGdRSFdPdmJCOXZzaHk3dwAAAAAAADiTFmc0QlJqSzhnUUhXT3ZiQjl2c2h5N3cAAAAAAAA4lhZnNEJSaks4Z1FIV092YkI5dnNoeTd3"
}
매 순회 요청마다 scroll 파라미터를 포함시켜 스냅샷 유지 시간을 갱신해야 합니다. 응답이 빈 배열을 반환하면 모든 데이터를 소진한 것입니다.
주의사항:
- Scroll 스냅샷은 메모리를 사용하므로,
scroll타임아웃을 불필요하게 길게 설정하면 리소스 낭비가 발생합니다. - Scroll은 실시간 데이터가 아닌, 초기화 시점의 스냅샷을 기반으로 동작합니다.
- 순회 요청 시 인덱스나 타입을 다시 지정할 필요가 없습니다.