책에는 안나오는 es(elasticsearch) 경험 맛보기

Runthe
NAVER Pay Dev Blog
Published in
7 min readJun 30, 2023

안녕하세요. 갓 아이 아빠가 된 금융 연동 개발 김환수입니다.

업무에 kotlin spring es 를 처음 사용하며 책에는 없었던 사용 경험을 공유드립니다. (제가 볼 때는 없었습니다..)

es 7.10 이후 라이센스 변경 공유

es 아파치 라이센스 아니였나..?

es 7.10 이후 버전부터는 라이센스 변경이 있습니다.

7.10 이후 oss 아파치 라이센스 버전이 제공되지 않습니다.

as a service 운영은 불가하고 on-premise 운영은 영향이 없습니다.

  • es x-pack 상용 기능 제공
  • aws es as a service 운영 형태 (es의 상용 기능 무단 제공)
  • aws es fork 한 뒤 opensearch를 공개

위 같은 과정이 일어나면서 라이센스 변경이 된 걸 알게 되었습니다. (라이센스 히스토리)

결과적으로는 사내 es 지원이 있어 es를 사용하게 되었습니다.

그러나 opensearch 도 es와 매우 유사한 api 를 제공하고 spring-data-opensearch 같은 라이브러리를 제공하고 있어

사용자 입장에서는 상황에 맞게 선택할 수 있는 선택지가 늘어난 거 같습니다.

es client 선택

es client 선택 시 아래와 같이 장단점이 있었습니다.

rest-high-level-client (8.x api client)

  • 장점
    - boot dependecy에 제약 없이 알맞은 client 버전 선택 가능
  • 단점
    - spring이 지원해주는 편의성 기능을 이용 못함

spring-data-elasticsearch

  • 장점
    - es source 타입 알맞게 변환해줌
    - flux 지원
    - 그 외 spring이 지원해주는 편의성 기능 (페이징 등등)
    - rest-high-level-client 를 기반으로 하기 떄문에 같이 병행 가능
  • 단점
    - boot dependency 로 인해 es 서버 버전에 맞는 client 를 사용하기 힘듬
    - es client 관련된 모든 기능을 spring이 제공해주지 않기 떄문에 복잡한 기능은 es client 같이 사용해야함

rest api

  • 장점
    - 가볍게 적용하기 좋고 dependency 영향이 없음
  • 단점
    - client api 혜택 못받음

서버 버전 호환성 이슈 및 서비스 요구사항이 es 의 다양한 기능을 필요로 했기 떄문에 rest-high-level-client를 사용하였습니다.

document 검색 10,000건 제한

es는 검색 default 건수가 분산 검색 효율, 메모리 이슈 등의 이유로 10,000건으로 제한되어 있습니다.

키바나에서 10,000건 까지 검색하고 다운로드할 수 있는 게 이런 제약사항 떄문인데요.

서비스 db로 활용하고자 하면 고민해 볼 만한 포인트입니다.

아래와 같이 해결할수 있습니다.

index level setting

  • max_result_window : 10000 -> 50000

search_after

  • unique 하면서 정렬이 가능한 값이 필요함, timestamp는 중복될 수 있음

적정 수준의 데이터양이어서 요구 사항이 생기면 unique 키값을 만들기 보단 max_result_window 설정을 하는쪽으로 계획 했습니다.

건수 제약사항의 또 다른 케이스로

검색 결과는 10,000건으로 조회 하고 전체 건수를 확인하고 싶을 떄는 다음과 같이

track_total_hits:true

옵션을 주셔야 합니다.

{
"track_total_hits": true
}

dynamic mapping explosion

데이터를 저장하다 보면 정해진 매핑이 아니라, 동적으로 매핑을 추가하고 싶은 경우가 있습니다.

바로 map 자료 구조 같은 데이터를 저장하는 케이스인데요. key가 동적이기 떄문에 저장 시에 index mapping에 무한으로 증가합니다.

{
"map":[
{
"prev":"naver"
},
{
"next":"financial",
}
...
...
]
}

그러면 아래와 같이 오류가 나타납니다.

reason=Limit of total fields [1000] has been exceeded

위 같이 무한으로 증가하는 경우를 dynamic mapping explosion 이라고 합니다.

이런 데이터를 저장하는 경우에 아래와 같이 방법들이 있습니다.

  • flattened 필드
    - 사용 버전에서는 상업 x-pack..
    - 다양한 type의 지원 없이 문자열 키워드로만 저장 가능
    - 기본적인 쿼리, aggregation 지원, 전문 검색 지원하지 않음
  • 필드수 제한을 변경
    - “index.mapping.total_fields.limit”: 1000 -> 20000
  • 동적 매핑이 발생할수 있는 필드들을 정해진 key, value 필드로 전처리 가공 + nested 필드 사용
    - nested object 10,000건 제약 사항 있음
{
"map":[
{
"key":"prev", "value":"naver"
},
{
"key":"next", "value":"financial"
}
]
}
  • 구분자를 통한 토크나이징 활용

flattened 필드를 이용해 보면 좋았겠으나 이런 케이스 대안으로

nested 필드를 사용하였는데요. 큰 이슈 없이 잘 활용 중입니다.

다만 nested 필드는 kibana에서 일반 매핑보다 집계 차트를 활용하기가 좀 어려운 점이 있습니다.

집계 차트를 잘 활용 해야 하고 동적 매핑 숫자가 급증하지 않는 케이스 한해서는 매핑 제한 설정을 변경 하는것도 괜찮을 거 같습니다.

timestamp 매핑

kibana 검색 까지 생각 하신다면 시간 필드에 offset 정보는 추가해주셔야 합니다.

kibana는 타임 스탬프 노출은 한국 시간으로 검색하지만 검색 조건은 UTC로 변환해서 검색 쿼리를 날려주기 떄문인데요.

위 케이스를 고려 하신다면 아래와 같이 저장할수 있는 방식이 있습니다.

  • 매핑
    - strict_date_optional_time||epoch_millis

저장포맷

  • epoch timestamp (long)
    - 사람이 읽기 어려움
  • iso8601 utc
    - 2023–06–16T01:26:12.665158Z
    - 읽기 어려움, UTC로 별도 변환 필요
  • iso8601 kst
    - 읽기 편함

글로벌 데이터는 아니라서 UTC 대신 kibana 에서 보기 편한 kst로 저장하였습니다.

단일필드 검색효율

속도를 향상 시키는 방법은 많겠지만, 일단 데이터가 많을 경우 여러 필드를 같이 검색하는거보다, 전처리 해서 단일 필드를 term 검색 했을떄

as is

"filter":[
{
"term":{
"a":{
"value":"a", "boost":1.0
}
}
},
{
"term":{
"b":{
"value":"b", "boost":1.0
}
}
},
{
"term":{
"c":{
"value":"c", "boost":1.0
}
}
}
]

to be

{
"term":{
"abc":{
"value":"a.b.c", "boost":1.0
}
}
}

rally 로 성능 테스트시 1.5 ~ 2배 정도 차이를 보았습니다.

document 건수나, 매핑에 차이에 따라서 다를수 있으니 이건 확인해 보시면 좋을거 같습니다.

각종 제약조건..그리고 끝

es는 각종 제약조건이 있습니다..

  • cluster.max_shards_per_node
    - 클러스터 노드당 저장할 수 있는 샤드 설정 기본값이 1,000

일별로 인덱스를 저장하고 레플리카 샤드 설정을 조금만 해줘도 금방 1,000개가 차기 때문에

해당 사항을 모른다면 장애가 날 수도 있습니다.

위와 같이 의심된다 싶으시면 공식 문서를 끼고 검색해 보는 게 좋은 거 같습니다.

이상으로 책에는 왜 이런 게 안 나오지? 하던 내용들을 몇 가지 간단히 담아봤습니다.

읽어주셔서 감사합니다.

--

--