Elasticsearch deep paging 문제
문제점
페이지 사이즈가 100 이고, 30 페이지를 검색하려고 하면, 3000 개의 문서를 정렬해야 하고 그 다음에 마지막 100 개의 문서를 반환하게 됩니다.
이 문제는 더 깊이 50 페이지 200 페이지로 들어갈 수록 더 많은 메모리를 사용해야 하고 결국은 Elasticsearch 가 죽는 결과를 가져옵니다.
인덱스가 여러 개로 나뉘어 있는 경우, 각 샤드별로 200 페이지 * 100 개 문서
를 정렬해서 100개의 문서를 반환하는데 페이지가 더 깊어지면 결국 검색에 참여하는 모든 노드가 메모리부족으로 죽게 됩니다.
해결책
-
원하는 결과를 1~5 페이지에서 찾을 수 있도록 score 를 잘 산정한다.
-
일정 페이지 이상으로 들어갈 수 없도록 막는다.
구글은 28 페이지(검색결과 280개)를 넘는 결과를 표시하지 않습니다.
결과셋의 최대값은
index.max_result_window
에 의해 결정되고 디폴트값은 10000 인데, 이 값을 줄여야 합니다. -
search_after
search_after
기능을 이용해 페이징과 유사한 기능을 수행할 수 있습니다.
sort
를 포함해야 하고, sort
에 포함된 파라미터 중 하나는 Unique Key 이어야 합니다.
curl -X GET "localhost:9200/items/_search?pretty" -H 'Content-Type: application/json' -d'{
"size": 2,
"query": {
"match" : {
"itemname" : "아이폰케이스"
}
},
"sort": [
{"lastupdate": "desc"},
{"itemid": "asc"}
]
}'
검색결과에 sort
가 포함된 것을 볼 수 있습니다.
{
"took" : 7,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 62,
"max_score" : null,
"hits" : [
{
"_index" : "items_20200630112027702310",
"_type" : "_doc",
"_id" : "1439684",
"_score" : null,
"_source" : {
"lastupdate" : "2020-06-30T11:21:57.000Z",
"itemname" : "[DISNEY]디즈니 카드수납 스윙 충격흡수 케이스(아이폰케이스)",
"@timestamp" : "2020-06-30T11:23:19.069Z",
"price" : 3000.0,
"regdate" : "2020-06-27T07:13:11.000Z",
"itemid" : 1439684,
"category" : "하드 케이스\r"
},
"sort" : [
1593516117000,
1439684
]
},
{
"_index" : "items_20200630112027702310",
"_type" : "_doc",
"_id" : "1452585",
"_score" : null,
"_source" : {
"lastupdate" : "2020-06-30T11:21:57.000Z",
"itemname" : "[MST]플라워 핸드백 퍼니케이스(아이폰케이스)",
"@timestamp" : "2020-06-30T11:22:19.668Z",
"price" : 3000.0,
"regdate" : "2020-06-27T07:13:11.000Z",
"itemid" : 1452585,
"category" : "소프트/실리콘 케이스\r"
},
"sort" : [
1593516117000,
1452585
]
}
]
}
}
포함되어 있는 마지막 sort
의 값을 아래와 같이 search_after
에 추가하여 검색하면, 위의 검색결과 이후의 문서를 반환받게 됩니다.
curl -X GET "localhost:9200/items/_search?pretty" -H 'Content-Type: application/json' -d'{
"search_after": [1593516117000, 1452585],
"size": 2,
"query": {
"match" : {
"itemname" : "아이폰케이스"
}
},
"sort": [
{"lastupdate": "desc"},
{"itemid": "asc"}
]
}'
위 기능으로 다음 페이지 다음 페이지 의 형식으로 페이징과 유사한 기능을 수행하게 됩니다.
그러면, 이전 페이지 또는 현재 페이지에서 5페이지 앞의 페이지는 어떻게 구해야 할까요?
-
Random access with search_after
search_after 에서 쓰이는 파라미터를 미리 구해서 저장해 놓고, 저장되어 있는 파라미터를 이용해 각 페이지에 접속하는 아이디어입니다.
하지만 저장해 놓은 파라미터를 계속 업데이트해야 하는게 쉽지 않겠네요.
비고
GS SHOP 은 쿨하게 18만개의 결과 를 다 표시하네요.
아마 scroll api 를 사용한 듯 합니다. 아니면… 머신 스펙을 많이 늘렸던지… 훔.