일관된 스코어링

원본 보기

일관된 스코어링

Elasticsearch가 샤드와 레플리카로 동작한다는 사실은 좋은 스코어링을 확보하는 데 있어 어려움을 더합니다.

동일한 사용자가 동일한 요청을 연달아 두 번 실행했는데 두 번의 결과에서 문서 순서가 서로 다르게 반환된다면, 상당히 좋지 않은 경험이 아닐까요? 안타깝게도 레플리카가 있는 경우(index.number_of_replicas가 0보다 큰 경우) 이런 일이 발생할 수 있습니다. 그 이유는 Elasticsearch가 쿼리를 보낼 샤드를 라운드 로빈 방식으로 선택하기 때문입니다. 따라서 동일한 쿼리를 연달아 두 번 실행하면 같은 샤드의 서로 다른 복사본으로 요청이 갈 가능성이 꽤 높습니다.

그렇다면 이것이 왜 문제일까요? 인덱스 통계는 스코어를 구성하는 중요한 요소입니다. 그리고 이러한 인덱스 통계는 삭제된 문서로 인해 같은 샤드의 복사본 간에 서로 다를 수 있습니다. 아시다시피 문서가 삭제되거나 업데이트될 때 기존 문서는 인덱스에서 즉시 제거되지 않고 삭제됨으로 표시만 되며, 해당 기존 문서가 속한 세그먼트가 다음에 병합될 때에야 디스크에서 제거됩니다. 그런데 현실적인 이유로 이렇게 삭제된 문서들도 인덱스 통계에 반영됩니다. 그래서 프라이머리 샤드가 삭제된 문서를 대량으로 제거하는 큰 병합을 막 마쳤다고 가정하면, 그 샤드의 인덱스 통계는 (여전히 삭제된 문서를 많이 가지고 있는) 레플리카와 충분히 달라질 수 있고 그 결과 스코어도 달라질 수 있습니다.

이 문제를 우회하는 권장 방법은 로그인한 사용자를 식별하는 문자열(예: 사용자 ID나 세션 ID)을 preference로 사용하는 것입니다. 이렇게 하면 특정 사용자의 모든 쿼리가 항상 동일한 샤드로 가게 되므로, 쿼리 간에 스코어가 더 일관되게 유지됩니다.

이 우회 방법에는 또 다른 이점이 있습니다. 두 문서의 스코어가 같을 때, 기본적으로 이들은 내부 Lucene 문서 ID(_id와는 무관합니다)로 정렬됩니다. 그런데 이 문서 ID는 같은 샤드의 복사본 간에 다를 수 있습니다. 따라서 항상 동일한 샤드로 요청이 가도록 하면 스코어가 동일한 문서들의 순서도 더 일관되게 얻을 수 있습니다.

내용이 동일한 두 문서가 서로 다른 스코어를 받거나 정확히 일치하는 문서가 1위로 랭크되지 않는 것을 발견했다면, 그 문제는 샤딩과 관련이 있을 수 있습니다. 기본적으로 Elasticsearch는 각 샤드가 자신의 스코어를 직접 산출하도록 합니다. 하지만 인덱스 통계가 스코어에 중요한 영향을 미치기 때문에, 이 방식은 샤드들의 인덱스 통계가 서로 비슷할 때만 잘 동작합니다. 기본적으로 문서는 샤드에 고르게 라우팅되므로 인덱스 통계가 매우 비슷할 것이고 스코어링도 기대대로 동작할 것이라는 가정이 깔려 있습니다. 그러나 다음과 같은 경우라면:

  • 인덱스 시점에 라우팅을 사용하거나,
  • 여러 인덱스를 대상으로 쿼리하거나,
  • 인덱스에 데이터가 너무 적은 경우

검색 요청에 관여하는 모든 샤드의 인덱스 통계가 서로 비슷하지 않아 연관성이 나빠질 가능성이 높습니다.

데이터셋이 작다면, 이 문제를 우회하는 가장 쉬운 방법은 모든 데이터를 단일 샤드를 가진 인덱스(index.number_of_shards: 1, 이것이 기본값입니다)에 인덱싱하는 것입니다. 그러면 모든 문서에 대해 인덱스 통계가 동일해지고 스코어도 일관되게 유지됩니다.

그 외의 경우, 이 문제를 우회하는 권장 방법은 dfs_query_then_fetch 검색 타입을 사용하는 것입니다. 이렇게 하면 Elasticsearch가 관여하는 모든 샤드에 먼저 한 번 왕복하여 해당 쿼리에 대한 인덱스 통계를 요청하고, 그 다음 코디네이팅 노드가 그 통계들을 병합한 뒤 샤드에 query 단계 수행을 요청할 때 요청과 함께 병합된 통계를 전달합니다. 그러면 샤드는 자체 통계 대신 이 전역 통계를 사용하여 스코어링을 할 수 있습니다.

대부분의 경우 이 추가 왕복 비용은 매우 저렴합니다. 다만 쿼리에 매우 많은 수의 필드/텀이 포함되거나 퍼지 쿼리가 포함된 경우에는, 통계를 조회하기 위해 모든 텀을 텀 사전에서 찾아봐야 하므로 통계 수집만으로도 저렴하지 않을 수 있다는 점에 유의하세요.