Elasticsearch 에서 Mapping 의 중요성을 느끼다.

안녕하세요.

제가 개인적으로 개발하고 있는 프로젝트가 있습니다.
https://github.com/LeeKyoungIl/illuminati Project illuminati 인데요.
요즘은 gatekeeper라는 서브 프로젝트를 열심히 개발 중이죠.
https://github.com/LeeKyoungIl/illuminati/tree/feature/gatekeeper_new

Project illuminati는 데이터를 결국 ES로 보내주어 그 데이터를 Kibana에서 사용하도록 만들었던 터라
ES로 save 만 할 뿐 Read 성 기능을 필요가 없었습니다. (어차피 Kibana에서 해주니…)

하지만 Kibana 도 제가 잘 다루지 못하고… 원하는 대로 데이터를 뽑지도 못해서 visualization 툴을 새로 만들고 있는데 그게 위의 Gatekeeper 프로젝트인데요.

여기서 직접 ES 데이터를 핸들링하려고 보니 ES에서 데이터를 읽어오는 기능이 필요하더라구요.
그래서 작업을 하는 중 일반적인 데이터의 검색은 별 문제가 없습니다. 아시다시피 간단한 Query로도 데이터를 쉽게 뽑아낼 수 있죠.

하지만 RDB에서의 Group By에 의한 값과 count를 구하는 Query와 동일한 결과를 뽑아야 하는데 쉽지 않더라구요.

(보통은 업무상에서 ES를 쓸 때 빠른 검색 결과를 뽑기 위해서 다양한 필드에 특정 키워드 검색 (날짜와 같이) 정도로 쓰지 ES를 이용해서 데이터 집계를 하는 용도로는 사용해 본 적이 없는 거 같네요.)

하지만 물론 집계 쿼리 자체는 어렵지 않습니다.

간단하게 aggregation query 만 날리면 뽑을 수 있죠… 하지만.. 저는 해보니 오류가…

저는 serverInfo 의 hostName field를 이용해 hostName 별로 호출된 수를 구하고 싶었는데 기본적으로 serverInfo.hostName의경우 text field로 설정이 되어있습니다. 이경우 집계 쿼리를 사용할 수 없나 봐요. (저도 공부 중이라..)

그래서 fielddata=true로 설정하라고 하네요. (메모리가 많이 사용될 수 있다고 합니다.)

이 방법 보다는 좀.. 찾아보니

이렇게.. 정렬이나 집계를 원하면 text 가 아닌 keyword로 설정을 하라고 되어있더라구요.

그래서 새롭게 index를 mapping 해주기 위해 쿼리를 날렸습니다.

결과는 오류…

오류가 난 이유는.. ES는 매핑을 update 할 수 없습니다. 만약 데이터가 있다면 update시 타입이 변경이 불가능하기 때문이죠.

그래서 index를 날리고 새롭게 맵핑을 해주었습니다.

다음 다시 집계 쿼리 실행!

제가 원하는 대로 데이터가 잘 나오는군요.

 

ES는 데이터를 입력 시에 자동으로 데이터를 기반으로 mapping을 생성합니다.

제가 이번에 테스트를 한 hostName는 제가 한것처럼 따로 mapping을 하지 않으면 데이터가 입력이 될때 text로 맵핑이 자동으로 된 걸 볼 수 있습니다.

그래서 ES에서는 맵핑을 직접 하는 것을 권장하는 것 같습니다.

그럼 하나하나 직접 이렇게 mapping query를 날려 줘야 하나?? 해서 만들어 보았는데요.

MappingType 이라는 enum을 만들었습니다.
https://github.com/LeeKyoungIl/illuminati/blob/feature/gatekeeper_new/illuminati-client/illuminati-client-common/src/main/java/com/leekyoungil/illuminati/common/dto/enums/MappingType.java#L3-L27

다음 해당 Mapping값을 사용할 GroupMapping 에노테이션을 하나 만들어 주었습니다.
https://github.com/LeeKyoungIl/illuminati/blob/feature/gatekeeper_new/illuminati-client/illuminati-client-common/src/main/java/com/leekyoungil/illuminati/common/dto/GroupMapping.java#L7-L13

디음은 제가 집계를 원하는 항목에 해당 에노테이션을 달아 주었습니다.
항목은 ServerInfo class의 hostName 과 serverIp입니다.
https://github.com/LeeKyoungIl/illuminati/blob/feature/gatekeeper_new/illuminati-client/illuminati-client-common/src/main/java/com/leekyoungil/illuminati/common/dto/ServerInfo.java#L25-L26

ES로 데이터를 전송할 class 를 시작으로 하위 클래스들을 전부 돌면서 해당 @GroupMapping 에노테이션과 GSON의 @Expose 에노테이션이 걸린 것들을 전부 찾아서 MappingBuilder 에 set 해주는 부분 입니다.

https://github.com/LeeKyoungIl/illuminati/blob/feature/gatekeeper_new/illuminati-client/illuminati-client-elasticsearch/src/main/java/com/leekyoungil/illuminati/elasticsearch/model/IlluminatiEsTemplateInterfaceModelImpl.java#L263-L286

이제 ES로 전송을 해야겠죠 이 부분에서 고민 많이 했습니다.

mapping 은 이미 생성되어있으면 수정이 불가능하기 때문에 ES로 데이터를 저장하기 전에 먼저 mapping 정보가 있는지를 먼저 체크를 한 다음 없다면 mapping을 만들어 주고 (@GroupMapping 에노 테이션을 걸은 것들만 생성이 먼저 날아가며 나머지는 최초 데이터 저장 시 자동으로 mapping 됨) 나서 다음 ES로 데이터를 저장하는 방식으로 일단 구현해 보았습니다.

이게 항상 ES에 데이터를 저장할 때 먼저 체크하는 호출이 날아가야 하기 때문에 정말 마음에 들지 않습니다만.. 일단 이렇게 한 뒤 차차 수정해 나가도록 할 생각입니다.

아무튼 로직은 아래와 같습니다.

ES 에 save 를 할때 먼저 index를 체크하고 없다면 만들어 준다. (원래 mapping 이지만 RDB에 친숙하게 index로 메서드 명을 만들었습니다.)

https://github.com/LeeKyoungIl/illuminati/blob/feature/gatekeeper_new/illuminati-client/illuminati-client-elasticsearch/src/main/java/com/leekyoungil/illuminati/elasticsearch/infra/ESclientImpl.java#L56-L57

ES에 해당 mapping 이 있는지 없는지 질의하고 없다면 index 를 생성해 주는 질의를 날리는 부분 입니다.

https://github.com/LeeKyoungIl/illuminati/blob/feature/gatekeeper_new/illuminati-client/illuminati-client-elasticsearch/src/main/java/com/leekyoungil/illuminati/elasticsearch/infra/ESclientImpl.java#L165-L173

제가 아직 ES를 잘 모르고 공부를 하는 중 이라.. 이렇게 만들어 보았는데요.

역시나… ES에 데이터를 저장 할때마다 mapping 을 체크 하는 부분이 걸리네요..

좋은 의견 있으면 부탁좀 드리겠습니다~

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다