Elasticsearch deep paging 문제

By | 2020년 7월 2일
Table of Contents

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 를 사용한 듯 합니다. 아니면… 머신 스펙을 많이 늘렸던지… 훔.

답글 남기기