Loading
2022. 4. 30. 22:33 - lazykuna

왜 ElasticSearch 인가?

ElasticSearch는 이미 엄청나게 유명하고 잘 알려진 시스템이지만, 개인적으로 복습 및 몇개 부족한 세부 개념을 보충하고자, 그리고 보다 폭넓은 관점에서의 생각을 같이 정리하기 위해 글로 씁니다.

그리고 데이터를 활용하는 입장에 있다 보니 점점 이런 것들에 대한 공부가 필요해지네요... 🤔

ElasticSearch는 무엇이고, 왜 쓰는가?

ElasticSearch는 분산형 검색 및 분석 엔진입니다. 개요에서 눈치챌 수 있다시피, 데이터의 분석(가공)을 통해 기존 방식의 검색 대비 훨씬 빠른 검색 성능을 제공합니다. 동시에, 필요하다면 분석된 데이터를 이용하여, 이에 대한 통계나 추가 분석을 해 볼 수도 있겠습니다.

이러한 데이터 수집, 가공, 검색, 및 분석 서비스들을 하나로 묶어 쓰기 쉽게 배포하기도 하는데, 이것이 바로 ElasticStack입니다. 물론 그 중에서도 핵심은 ElasticSearch이니 지금은 그것만 보는 걸로...

ElasticSearch 없이 검색하면 그렇게 오래 걸리나요?

ElasticSearch 없이 텍스트를 검색하면, 데이터베이스를 풀스캔해야 하고, 그 데이터를 기반으로 텍스트 매칭 검색을 수행해야 합니다. 데이터가 몇GB만 되어도 검색에 수행되는 시간은 거의 몇분 단위로 늘어납니다. 이건 일반 사용자들이 감내하기 어려운 수준입니다.

반면 ElasticSearch는 미리 가공/분석된 데이터를 통해서, 데이터베이스 풀스캔 없이 빠르게 원하는 데이터에 접근 가능합니다. 가장 적절한 비유로는 DB의 인덱스와 흡사한 역할을 한다고 볼 수 있습니다.

텍스트 검색을 위한 데이터 모델링: 역 인덱싱

아주 잘 알려져 있다시피, ElasticSearch는 데이터를 효율적으로 조회하기 위한 방법으로 역 인덱싱 설계를 사용하였습니다. 어떤 문서를 ElasticSearch에 “분석"을 맡기면, 해당 문서가 사용하는 단어의 목록을 추출할 수 있습니다. 이른바 “문서 — 단어"의 조합이 나오는데, 이를 역으로 “단어 — 문서" 조합으로 만들어 저장하면 최종적으로 검색을 위한 역 인덱싱 가공이 됩니다.

대충 위 그림처럼요. 문서를 분석한 후, 분석한 내용을 DB에 반영하는 과정이 수행된 모습입니다.

  • 물론 한국어 같은 경우는 글자 그대로를 사용하기보다는 추가적으로 형태소 분석 등의 작업을 수행하게 됩니다.

저렇게 저장하면 삭제(업데이트)할때 좀 힘들지 않을까요?

실제로 그렇습니다. 특정 Document를 삭제(업데이트)하겠다고 ElasticSearch에 통보하면, 해당 document와 연관되는 Term들을 모조리 찾아서 업데이트를 수행하게 됩니다.

삽입/수정/삭제 시간이 비싸고 오래 걸리는 건 분명 단점입니다. 그럼에도 불구하고 텍스트나 BLOB가 크고, 이들에 대한 Search의 비중이 높다면 ElasticSearch는 충분히 사용할 가치가 있겠습니다.

다른 형태로도 데이터 모델링이 가능하지 않을까요?

네 가능합니다. 실제로 현실의 데이터 모델은 저렇게 간단하지는 않죠.

진료 환자만을 예로 들어봐도, 들어가야 하는 필드가 {이름, 나이, 생년월일Date, 주민등록번호} 정도는 있어야 할 겁니다. 가끔은 그 환자에 진료 데이터가 Nested하게 붙어서 들어갈 수도 있고요.

그렇게 쓰이는 데이터 모델링들에 대해서 ElasticSearch에서는 기본적인 검색기능을 제공합니다. 흔히 이야기하는 4가지의 관계형 데이터 모델링에 대해서요. 워낙 유명한 개념인듯 해서 이 글에서는 깊게 설명 안 하고 대충 넘어가지만, 비단 ElasticSearch 뿐 아니라 어디든지 사용 가능한 모델링이라 짚고 넘어갈 필요는 있습니다.

  • Parent-Child 모델링
    엘라스틱서치 자체의 Join 방법을 사용하는 방법입니다. Parent와 Child 각각에서 수정이 빈번할 때 활용될 만 하지만, 검색 비용이 다소 비싸다고 합니다.
  • Nested 모델링
    하나의 문서 안에 관계 정보를 포함하는 객체를 삽입하여 저장합니다. 이를테면 블로그 글과 이에 대한 여러 댓글 같은 식으로 많이 비유되죠. Parent-Child와의 차이점은 엔티티가 완전하게 분리되어 있지 않다는 점...?
    • 내부 처리 방식에 대해서는, ElasticSearch는 따로 nested에 대해서 데이터 구분 없이 flatten해서 처리한다고 합니다. 의외로 단순하게 만들어져 있음 ㅎㅎ... 참조#
  • Application Side Join 모델링
    말 그대로 관계있는 데이터를 application 단에서 직접 긁어와서 같이 활용하도록(Join) 하는 설계입니다. 쿼리를 여러번 호출해야 하는 특징이 있어, 이에 따른 부하가 어느 정도가 될 지 잘 고려해서 설계할 필요가 있습니다.
  • Denormalization 모델링
  • 엘라스틱서치 본연의 방식**입니다. 걍 데이터 중복되든 말든 신경 안 쓰고 무조건 추가해 넣는 개념입니다. 속도가 극한으로 중요한 경우에 고려해 볼 만 합니다. *(뭐, 중복된 데이터는 주기적인 scheduler 통한 batch 작업 같은걸로 정리해 볼 수도 있고요)

그래도 내부 구조같은 걸 따져보면, 결국은 역 인덱싱이 근본입니다.

그럼, 저걸로 실제 검색은 어떻게 하죠?

간단한 단어 같은 경우에는 검색이 아주 쉽습니다. 앞에서 만들어진 term index를 사용하는 것으로 충분합니다.

복잡한 문장 같은게 들어오면요?

문장 자체에 대해서 선분석을 한 후, 이를 이용하여 ElasticSearch Term index로부터 결과들을 가져오고, 이를 조합하여 유의미한 결과를 보여줍니다.

Elastic 가이드북에 써 있는 대로 “match”를 예를 들면, “quick fox”를 검색하는 경우 “quick”와 “fox”두 글자에 대해서 문서들을 가지고 온 뒤에, 이들의 교집합에 해당하는 문서들을 결과값을 가지고 올 수 있을 것입니다. 혹은, “match_phrase”와 같이 순서까지 고려하여 검색하도록 함으로서 더 정확한 결과를 가지고 올 수 있을 것입니다. 대신 이 경우에는 단어의 위치 값까지 추가로 DB에 들어가야 할 수도 있겠죠.

왜 NoSQL과 엮이는가?

딱히 정답은 없는 내용이지만, 이 정도가 아마 이유가 아닐까 싶습니다.

  • 분산시스템
    NoSQL은 잘 알려져있다시피 분산시스템으로 활용이 가능합니다. DB를 분산시스템으로 구성함으로서 위에서 언급한 비싼 삽입/수정/삭제 에 대한 컴퓨팅 비용으로 인한 병목을 최소화 할 수 있을 것입니다.
  • RDBMS의 기능이 굳이 필요하지 않음
    자체적으로 Lucene 인덱싱을 사하용요 있고, Eventually consistent 할 수 있도록 잘 관리해주기 때문에 굳이 join, FK 등을 사용하지 않습니다. 아니면 denorm같이 유저가 알아서 관리하도록 하거나...

사실 이러한 전반적인 이유들의 근거는 성능을 우선함에 따라 나온 것이라고 봅니다. 검색 결과가 약간은 부정확할 수 있더라도 그 정도는 어렵지 않게 용납할 수 있을 것이라 보았고, 대신 빠른 반응성을 위해서 성능을 우선순위로 둔 것이겠죠. B2C 서비스의 경우 응답성이 굉장히 중요하다는 것은 이미 많은 연구결과들이 뒷받침해주고 있습니다.

이걸로는 텍스트밖에 검색 못하나요?

아닙니다. 텍스트가 아니더라도 적당하게 feature를 저장할 수 있다면 ElasticSearch가 검색할 수 있습니다. 심지어는 이미지 검색도 할 수 있습니다!

다만, 이를 위해서는 현재 다루는 내용에서 조금 벗어난 범주의 이야기들과 전제사항이 필요합니다. 간단하게만 훑어 놓겠습니다.

이미지를 표현하는 방법?

머신 러닝의 발전에 힘입어, 이미지를 훌륭한 벡터로 표현하는 모델과 방법 등에 대해서는 이미 수많은 논문이 있습니다. 즉 이미지를 정규화된 벡터로 표현하고, 이를 기반으로 유사한 백터를 찾을 수 있다면 충분히 검색 할 수 있을 것입니다.

문제는 이를 검색하는 방법입니다. 벡터값은 역 인덱싱으로 가공할 수 없는 연속된 값이기 때문에, 다른 방식의 접근을 선택해야 합니다. 일반적으로는 Cosine Similarity를 계산하여 최적의 검색결과를 찾을 수 있겠지만, 소요되는 cost가 어마어마 할 것입니다. 그래서 검색을 위해서 아래 방법들을 활용합니다. (주 : 제시한 내용들은 최선이 아닙니다!!)

  • KNN을 통한 검색
    그런데 이를 위해서 미리 벡터 색인 작업을 하는 것 자체가 어떻게 보면 역 인덱싱과 굉장히 흡사한 면이 있습니다! 또한, 색인된 벡터 값을 기반으로 해당 이미지가 어떤 특징을 가지고 있는지 간접적으로 레이블링 할 수 있을지도 모릅니다(예: 고양이, 개, 자동차 등). 추가 분석의 여지가 생기는 거죠.
    그리고 최근 ElasticSearch에서 KNN 검색을 지원하고 있으니, 활용해볼 수 있겠습니다.
  • PCA를 통한 차원 축소
    벡터 크기가 크고 sparsity 등을 감안했을 때 견적이 나오면 해볼 수 있겠죠.

KNN 이외에도 ANN, AKNN 등 수없이 많은 벡터 검색 알고리즘들이 있습니다. 그리고 feature extraction 방식에 따라서도 성능이 많이 갈리기 때문에, 개략적인 아키텍처는 짤 수 있을지라도 정답은 없지 않을까 싶네요.

  • 한가지 재미있는 점은, 이러한 벡터 기반의 검색은 최근 텍스트 검색에서도 활용되고 있는 경향이 커지고 있습니다. 이를 통해 얻을 수 있는 이점은 유사한 의미의 텍스트가 있어도 검색을 잘 해줄 수 있다는 이점이 있다는 것이죠. 이를테면 “How to install python package”라고 검색하면 “Using pip3”, “How to use setuptools”와 같은 문서가 같은 단어가 없음에도 불구하고 연관성 있는 문서로 뜨게 될 것입니다. 역 인덱싱으로는 이러한 결과를 도출해내기 어렵죠.

참고