Elasticsearch 검색 속도 튜닝

JunMyung Lee·2023년 10월 20일
0

Elasticsearch

목록 보기
28/37

Elasticsearch를 운영하면서 검색 속도를 높일 수 있는 방안에 대해 찾아보았다.
[HowTo] 검색 속도 튜닝 : 네이버 블로그 (naver.com)

해당 글이 가장 보기 좋았고, 이 글에서 필요한 부분을 작성하고자 한다.

파일시스템 캐시 메모리

Elasticsearch 클러스터를 구성할때 흔히들 서버 메모리의 반은 남겨야 한다라고들 한다. 여러 이유가 있겠지만 한가지 이유가 파일시스템 캐시를 사용하기 위함이다. ES 인덱스의 접근 빈도가 높은 데이터들을 물리 메모리에 보관할 수 있어야 한다.

Filter? Must?

filtermust의 차이는 다음과 같이 두가지다.

  • 캐싱 여부
  • 스코어링 여부

여기서는 캐싱이 중요한데, 동적으로 지속적으로 변하는 값을 filter로 넣어 캐싱을 사용해 버리면 실제 필요한 캐싱 부분이 사라져서( 업데이트 되어서 ) 속도와 부하가 일어날 수 있다.

스코어링을 하지 않는데 그럼 must를 사용해야할까?

상황에 따라 다르지만 function_score - constant_score 를 사용해서 지정된 스코어링을 부여하여 처리하는 방식으로 하자.

function_score는 복잡한 스코어링 로직을 구현하고자 할 때 사용되며, constant_score는 간단한 필터 기반의 결과를 얻고자 할 때 사용

빠른 하드웨어 사용

처리속도가 빠른 SSD의 로컬 스토리지를 해야한다. 다음과 같은 경우 처리, 속도에 문제가 생겨 I/O가 발생 할 수 있다.

  • NFS, SMB과 같은 원격 파일시스템의 사용
  • Amazon EBS와 같은 가상 저장소
  • HDD 디스크

Document 모델링

Document 설계시 Join 관계는 설정하지 말자. Nested는 쿼리를 몇배 느리게 만들며, Parent-Child 관계는 쿼리를 수백배 느리게 만든다. 따라서 Join 관계가 없는 단순한 구조로 만들자.

가능한 적은 필드를 검색

query_string이나 multi_match는 많은 필드가 포함될 수록 느려진다. 인덱싱 시점에 그 값들을 단일 필드에 복사해서 검색을 수행하자(Multi-match best-fields).
copy-to 매핑 지시자를 사용해서 처리하도록 하자.

{
  "properties": {
    "name_and_hq": {
      "type": "text"
    },
    "name": {
      "type": "text",
      "copy_to": "name_and_hq"
    },
    "hq": {
      "type": "text",
      "copy_to": "name_and_hq"
    }
  }
}

Aggregation용 데이터 색인

문서에 Age필드가 있고 고정된 범위로 Range Aggregation을 수행해야 할 때, 미리 범위값을 지정하여 색인하면 빠르게 Aggregation 처리가 가능하다.

예) 회원의 나이 비율을 구하고자 할때, 10대, 20대, 30대 등등으로 미리 색인필드를 따로 구성해 두자

Keyword 필드 매핑 고려

range 쿼리를 사용하지 않는 필드에 대해서는 숫자타입으로 되어있는 필드도 Keyword 타입으로 지정하자. term 쿼리에서는 keyword의 검색이 가장 빠르다.

스크립트 피하기

script 기반의 검색은 최대한 피하자. 하지만 필요한 경우가 많은데 이때는 내장 script로 등록해서 사용하자. 정확히는 모르지만 검색 쿼리로 표현하는 검색과 내장 스크립트로 넣어서 검색하는 방식이 다르다고 알고 있다.

내장 스크립트로 등록이 되면 만들어진 클래스 파일로 동작하고, 검색식에 스크립트를 넣으면 검색 마다 컴파일해서 처리한다고 들은 "기억"이 있다.

반올림된 날짜로 검색

now를 사용하는 쿼리는 매칭 결과가 매번 변해서 대체로 캐싱이 될 수 없다. 하지만 변하지 않는 값으로 변경해서 처리한다면, 많은 효율이 올라갈 것이다. 시간이 필요없고 날짜만 필요한 경우에는 format을 변경하여 처리하자.

force-merge

하나의 세그먼트로 force-merge된 샤드는 더 단순하게 사용이 가능하며, 검색 수행에도 더 효율적인 구조가 된다.

기존에 몰랐던 내용이라 복사 붙여넣기

Warm up global ordinals

Global ordinals 는 집계 성능 최적화를 위해 사용되는 자료구조이다. lazy 하게 계산되며 field data cache 의 일부로 JVM 힙에 저장된다. bucket 집계에 무겁게 사용되는 필드는, ES에게 request 를 받기 전에 global ordinal 을 생성하고 캐싱할 수 있도록 지시할 수 있다. 힙 사용량을 증가시키고 refresh 시간을 늘릴 수 있기 때문에 신중하게 적용해야 한다. 이 옵션은 이미 존재하는 매핑에 동적으로 eager_global_ordinals 를 넘겨서 설정할 수 있다.

{
  "mappings": {
    "properties": {
      "foo": {
        "type": "keyword",
        "eager_global_ordinals": true
      }
    }
  }
}

파일시스템 캐시 warm up 하기

만약 ES 가 돌아가는 장비가 재시작되었다면 파일시스템 캐시는 빈 상태일 것이고, 속도 향상을 위해 OS가 자주 접근되는 인덱스 (hot regions of the index)를 메모리에 불러오는데 약간의 시간이 소요될 것이다. index.store.preload 설정을 통해 OS에게 어떤 파일들을 적극적으로 메모리에 올려놓을 것인지 파일 확장자를 명시적으로 지정할 수 있다.

너무 많은 인덱스나 파일을 파일시스템 캐시로 넣는 경우 파일 시스템 캐시의 크기가 충분하지 않다면 속도를 더 느리게 할 수 있으니 조심해야 한다.

index sorting 으로 결합 (conjunction) 성능 향상

인덱싱 시점에 샤드 내 세그먼트가 정렬되서 저장되도록 하는 index sorting 을 통해 인덱싱 성능을 약간 희상해면서 a AND b 와 같은 결합을 빠르게 할 수 있다. 이는 a AND b 와 같은 표현식에서 a 가 일치하지 않으면 b와 같은 뒤에 남은 내용들은 스킵하는 결합(conjunction)의 특징 덕분이며, 좀더 자세한 내용은 index sorting 문서를 참고하라.

preference 를 사용해 캐시 사용 최적화

파일시스템 캐시, request 캐시, 쿼리 캐시 등과 같은 다양한 캐시를 통해 검색 성능을 높일 수 있다. 하지만 모든 캐시가 같은 노드 레벨에서 유지되는게 아니며, 같은 요청을 연달아 2번 보냈어도 기본 라우팅 알고리즘인 라운드 로빈으로 서로 다른 샤드로 요청이 보내져서, 노드 레벨에 존재하는 캐시를 충분히 누리지 못하게 될 수도 있다.

검색 어플리이션에서 유저가 비슷한 요청을 연달아 보내는 것이 일반적이기 때문에, preference 값을 사용해 현재 유저 또는 세션이 캐시를 써서 최적화할 수 있는지 구분할 수 있다.

ex) GET index/_search?preference=_only_local

0개의 댓글