본문 바로가기

ElasticSearch

[ElasticSearch] 비슷한 옵션 간의 차이와 적절하게 사용하기

"기초부터 다지는 ElasticSearch 운영 노하우" 책을 보면서 개발 시, 알아두어야 할 내용 정리


From/Size vs Scroll

  • From/Size vs Scroll
    • From/Size
      • 해당 옵션으로 pagination을 구현하면 pagination 도중에 문서가 추가로 인입되고 해당 문서가 검색에 포함될 경우, pagination 결과가 달라 질 수 있다.
    • Scroll
      • 중간에 문서가 인입되어 검색 결과가 달라진다고 해도 scroll_id가 유지되고 있는 동안에는 pagination에 포함되지 않기 때문에, pagination 결과가 달라지지 않는다
        • Scroll_id의 유지 기간은 노드의 힙 메모리에 영향을 주기 때문에 지나치게 설정하면 OOME 발생
      • 검색 결과가 동일하게 유지되어야 하는 pagination, 혹은 대량의 배치 작업에 주로 사용

bool query를 이용해 쿼리 조합하기

Bool query의 종류에는

  • must
    • 스코어링 O / 캐싱 X
  • filter
    • 스코어링 X / 캐싱 O
  • should
    • 스코어링 O / 캐싱 X
  • must_not
    • 스코어링 X / 캐싱 O

Bool query의 must, should는 Query Context에서 실행되고
filter, must_not은 Filter Context에서 실행된다.

Must 절에서는 match 쿼리로 특정 단어를 찾고, filter 절에서는 range 쿼리로 특정 날짜의 문서를 찾는다고 가정해보자.

  ...
  ...
    "bool" : {
        "must": {
            "match" : {}
        },
        "filter": [
            {
                "range" : {}
            }
        ]
    }
 ...
 ...

여기서 한 가지 주의할 점은 filter context에 포함되는 쿼리들은 filter 절에 넣는 것이 좋다. range 쿼리를 filter 절이 아닌, must 절에 포함시켜도 동일한 검색 결과가 나오지만, 조회 성능면에서는 filter 절에 range 쿼리 (적절한 Context 영역 사용) 사용한 것이 더 빠르다.

  • filter 절에서 실행된 range 쿼리는 캐싱의 대상이 되기 때문에 결과를 빠르게 응답 받을 가능성이 높다.
  • must 절에 포함된 Filter Context들은 score를 계산하는 데 활용되기 때문에 불필요한 연산이 들어가게 되지만,
    filter 절에 포함되면 filter context에 맞게 score가 계산이 되지 않는다.

T) match는 Query Context, range는 Filter Context에 해당


색인 성능 최적화

refresh_interval 변경하기

elasticsearch가 '준 실시간 검색 엔진'이라 불리는 것은 'refresh_interval'이 1초로 설정되어 있어 문서가 색인되고 1초 후에 검색이 가능하기 때문이다. 하지만 refresh 작업은 디스크 I/O를 발생시키기 때문에 성능 저하시킬 수 있다. 그렇기 때문에 사용하고자 하는 용도에 맞게 refresh_interval을 적절히 조절할 필요가 있다.

실시간 검색 엔진으로 사용하고자 한다면 refresh_interval을 기본 값인 1초로 설정하고,
대용량의 로그를 수집하는 것이 주된 목적이고 색인한 로그를 당장 검색해서 사용할 필요가 없다면 refresh_interval을 충분히 늘려서 색인 성능을 확보하자.

검색 성능 최적화

Elasticsearch 캐시의 종류와 특성

  • Node query cache
    • 쿼리에 의해 각 노드에 캐싱되는 영역
  • Shard request cache
    • 쿼리에 의해 각 샤드에 캐싱되는 영역
  • Field data cache
    • 쿼리에 의해 필드를 대상으로 각 노드에 캐싱되는 영역

Node Query Cache

filter context에 의해 검색된 문서의 결과가 캐싱되는 영역이다.
검색 요청 쿼리를 작성할 때 최대한 filter context를 활용하여 Node Query Cache에 문서가 캐싱될 수 있도록 하면 캐시의 효과를 활용하여 더 빠른 검색이 가능해진다.

그러니 `filter context를 적용할 수 있는 조건이라면 꼭 filter context를 활용하도록 하자.`
  • 해당 캐시는 한 번의 쿼리에 대해서는 캐싱되지 않고, 여러 번 실행해야 캐싱된다.
    동일한 쿼리를 여러 번 받은 노드들은 Query Cache Memory에 해당 문서를 캐싱하고, 이후에는 메모리 영역에서 데이터를 리턴

Shard Request Cache

Node Query Cache 영역과 달리 문서의 내용을 캐싱하는 것이 아니라, 집계 쿼리의 집계 결과 혹은 ReuqestBody의 파라미터 중 size를 0으로 설정했을 때의 쿼리 응답 결과에 포함되는 매칭된 문서의 수에 대해서만 캐싱한다.

즉, Node Query Cache가 검색 엔진에서 활용하기 적합한 캐시 영역이라면,
이 Shard Request Cache는 분석 엔진에서 활용하기 적합한 캐시 영역이라고 할 수 있다.

다만, 이 영역은 문서 색인이나 업데이트를 한 이후 refresh를 통해 샤드의 내용이 변경되면 기존에 캐싱된 결과가 초기화된다.
따라서 계속해서 색인이 일어나고 있는 인덱스에는 크게 효과가 없다.

Shard Request Cache 설정을 기본으로 활성화한 다음, 색인이 종료된 과거 인덱스는 request_cache를 true로 집계하고
색인이 한참 진행 중인 인덱스는 false로 집계하는 방식으로 사용하면, 과거 인덱스에 대해서는 캐싱 데이터를 리턴해서 빠르게 결과를 전달받고,
색인이 빈번하게 진행 중이어서 캐싱이 어려운 인덱스는 불필요하게 캐싱하는 낭비를 막을 수 있다.  
과거 인덱스에 색인이 들어오면 캐싱된 데이터가 초기화되기 때문에 인덱스를 쓰지 못하도록 read only 처리하는 것도 캐싱데이터를 유지시킬 수 있는 방법

Field data cache

주로 검색 결과를 정렬하거나 집계 쿼리를 수행할 때 지정한 필드만을 대상으로 해당 필드의 모든 데이터를 메모리에 저장하는 캐싱 영역

  • text 필드 데이터 타입에 대해서는 기본적으로 캐싱을 허용하지 않는다.
    • text 필드 데이터 타입은 다른 필드 데이터 타입에 비해 캐시 메모리에 큰 데이터가 저장되기 때문에 메모리를 과도하게 사용하게 되므로, 사전 방지
  • 집계를 수행한 필드의 모든 데이터를 메모리에 로딩하기 떄문에 집계 시에 불러들일 데이터의 양을 고려하여 사용해야 한다.

검색 쿼리 튜닝하기

캐싱을 이용한 성능 향상도 중요하지만 결국 쿼리를 어떻게 만드느냐가 가장 중요하다.
검색 성능을 떨어트리는 요인 중 하나는 너무 많은 필드를 사용하는 것이다.

물론 처음에 인덱스의 매핑 정보를 생성할 때에 우선적으로 불필요한 필드들을 제외해야 하지만 매핑 구조에 따라 필드가 많아지는 경우도 있다.
이런 경우는 별수 없이 많은 필드에 걸쳐 검색을 해야하는 경우도 생긴다. 이렇게 많은 필드를 하나의 필드로 모아서 검색할 수 있는 기능이
copy_to 기능이다.

first_name 필드와 last_name 필드와 같이, 검색할 때마다 두 개의 필드를 항상 사용해야 한다면
match 쿼리를 두 번 사용하는 것보다는 copy_to를 사용하여 하나의 필드에 검색하는 것이 검색 성능에 더 좋다.

가능하면 처음에 매핑 스키마 계획을 세울 때 최소한의 필드를 사용할 수 있도록 하고, 불가피하게 많은 필드들을 대상으로 검색해야 한다면
copy_to 기능을 최대한 활용하자.


불필요하게 사용되는 쿼리에 대해 살펴보자.
문서를 검색할 때 match 쿼리를 많이 사용한다. match 쿼리는 Query Context에 속하는 쿼리이기 때문에 analyzer를 통해 검색어를 분석하는 과정이 포함되기 때문에 분석을 위한 추가 시간이 필요하다.
반면에 Filter Context에 속하는 term 쿼리는 검색어를 분석하는 과정을 거치치 않는다.

따라서, match 쿼리보다 term 쿼리가 성능이 더 좋다.
- keyword 필드 데이터 타입으로 매핑된 문자열 필드는 match 쿼리보다 term 쿼리를 사용하는 것이 성능상 더 유리
- match 쿼리는 analyzer를 통해 검색어를 분석하는데, keyword 필드는 분석하지 않는 필드이기 때문에
검색할 때도 검색어를 분석하지 않는 term 쿼리가 더 적합
또한, Elasticsearch에서는 수치 계산이 없는 숫자형 데이터는 keyword 필드 데이터 타입으로 매핑하도록 권고한다.
    예) 회원의 고유 번호나 계좌 번호 같이, 숫자형 데이터지만 더하거나 빼는 연산이 필요없는 데이터  

이러한 데이터는 주로 값이 정확히 일치하는 문서를 찾는 쿼리를 작성하게 되기 때문에 keyword 필드 데이터 타입으로 정의하고
term 쿼리를 사용하는 것이 적합

숫자형 데이터는 주로 연산이나 range 같은 범위 검색 쿼리에 최적화되어 있기 떄문에 같은 숫자형이라고 하더라도 용도에 따라 적합한 데이터 타입을 정의하고 검색 쿼리를 작성하는 것이 좋다.