기본 콘텐츠로 건너뛰기

Full-text Search (Hibernate Search)

Full-text Search


서점 관리 프로그램에선 책이름을 데이터 베이스에 저장하고 있습니다. 책 중에 Refactoring: Improving the Design of Existing Code라는 책이 있다고 한다면 사용자는 책의 정확한 제목보다는 비슷한 단어, 예를 들어 refactor, refactors, refactored , refactoring과 같은 단어를 사용하여 책을 찾고 싶어할 것입니다. 이와 같은 검색 기법을 “Word stemming”이라고 한다. 한국말로 “어간 추출”이다. 즉, 어근이 같은 관련 단어들을 이용하여 찾아내는 것입니다.

Full-Text 검색은 텍스트를 다양한 방식으로 미리 분석하여 인덱싱을 해놓고 빠르게 자료를 검색해내는 기법을 말합니다. 위의 예와 같이 어근일 수도 있고, 비슷한 의미의 단어일 수도 있고, 반대말일 수도 있습니다. 한가지 공통된 특징은 텍스트를 검색 대상으로 하며, 사전에 관련 데이터를 가지고 인덱싱이 되어 있어야 한다는 것입니다.
이렇게 편리하고 강력한 기능을 지원해주는 Lucene이라는 라이브러리가 있습니다. 여기에 사용성을 더하기 위해서 ORM 프레임워크 Hibernate를 적용한 Hibernate Search ORM에 대해서 이야기해보려 합니다.



Hibernate Search의 Full-text Search

Hiberanate-Search는 Lucene을 사용하여 Full-text Search 기능을 제공합니다. 가장 큰 장점은 ORM을 통해 손쉽게 설정, 인덱싱 및 쿼리가 가능하다는 것입니다. 궁합이 잘 맞는다고 할 수 있습니다.


Lucene이란?


Full-Test 검색은 Hibernate가 지원해주는 것이 아니라 Lucene이라는 라이브러리를 사용하는 것입니다. 그렇기 때문에 기능을 사용하기 위해서는 먼저 Lucene이 무엇인지를 알아야 합니다.
Lucene은 인덱싱/검색 라이브러리입니다. 특징은 문자열만을 입력으로 받는다는 것이다. 문자열만을 입력으로 받기 때문에 문서 형식이 HTML,XML, PDF, Work이든 상관없이 분석이 가능합니다.



Tokenization

Lucene은 긴 텍스트 문자열을 분석합니다. 이를 분석하기 위한 사전 작업이 바로 긴 문자열을 작은 단위로 나누는 것입니다. 나누는 작업을 “Tokenization”, 토큰화라고 합니다. 토큰을 기준으로 인덱싱을 하게 됩니다.

다음과 같은 post-tokenization을 예로 들 수 있습니다. 이런 기능들을 통해 구글에서 검색하는 것과 같은 효과를 얻을 수 있습니다.
  1. Stemming – 어근별로 인덱싱을 하는 것입니다. 영어 기반 라이브러리가 잘 되어 있기 때문에 예를 들면 “자전거를 타다”라는 의미를 갖는 “bike”는 bikes 와 같이 실제 문장에서 쓰입니다. 이런 경우 “Stemming” Tokenizer는 bike로 바꿔서 인 덱싱합니다. can find both documents containing bike and those containing bikes.
  2. Stop Words Filtering – 의미를 이해하는데 불필요한 단어를 제거합니다. 이를 통해 인덱스 사이즈를 줄일 수 있습니다. 영어를 예를 들면 a , “the” 등과 같은 관사가 이런 부류에 속합니다.
  3. Text Normalization – 영어의 경우에 쓰이는데 액센트 등과 같이 기타 정보를 표시하는 문자를 본래 문자로 변환하여 줍니다. 의미에 영향이 없는 정보를 없애는 Normalization 과정입니다.
  4. Synonym Expansion – 유의어를 인덱스에 추가해줍니다. 유사 단어를 사용하여 검색할 수 있게 해줍니다.


Core Analysis

Lucene의 Analyzer 클래스를 설정하여서 다양한 텍스트 인덱싱하여 검색할 수 있습니다.
  1. Analyzer – 인덱싱과 검색에 쓰이는 TokenStream 을 생성해주는 기본 클래스입니다.
  2. CharFilter – CharFilter 은 토큰화하기 전에 문자를 일괄적으로 변경할 때 쓰입니다. 변경된 문자와 offset(변이, 위치 차이) 값을 계산하게 됩니다.
  3. Tokenizer – 입력되는 텍스트를 Token으로 나누는 역할을 합니다. 대부분의 경우 Analyzer의 첫번째 필터로 Tokenizer를 사용합니다. (만약 사전에 변환 작업이 필요하다면 앞에 나온 CharFilter subclass 를 먼저 사용하면 됩니다.)
  4. TokenFilter – A TokenFilter 는 Tokenizer.에 의해 생성된 토큰에 다음과 같은 변경을 적용할 때 쓰입니다.(deletion, stemming, synonym injection, case folding)

    지원하는 언어가 제한적이라는 것이 문제입니다. 그래도 한글에 대한 프로젝트들이 간간히 보입니다.


Performace

실제 프로젝트에 사용하기 위해서 lucene이 기존 시스템에 미치는 영향을 분석해야 합니다. Lucene을 적용할 경우 변경되는 사항은 바로 indexing 관련된 추가적인 Disk/CPU 사용입니다.


Indexing File size

먼저 Storage 사용량을 측정해보았습니다.

Indexing Speed

  • 4284.989746 documents/second
  • Reindexed 10200 entities
  • End indexing during 4719ms
  1. Speed
    • 개발 노트북 intel i5
    • 초당 4800 (documents/sec)
  2. Space (1000만건 일 경우)
    • whole re-indexing = 2083(sec) = 34분
    • 4.28MB * 1000 = 4280MB = 4.2 GB + a = 5GB
    • 20% space 증가 : Database Index 삭제시 가능(DB indexing 파일 줄어듬)
  3. 적용 방안
    • reindexing은 특별한 경우에만(업데이트)
    • near-realtime indexing으로 충분
    • 실시간 이벤트의 경우 실제 DB 접근 불필요


예제 프로젝트

백문이 불여일코딩이죠. Spring boot + REST + Hibernate Search ORM 을 활용한 샘플 코드를 사용하여 간단히 테스트해 볼 수 있습니다.

https://github.com/atinjin/hibernate_search_example

댓글

이 블로그의 인기 게시물

AWS ELB 504 Error

AWS EC2  운영 중 가끔씩 볼 수 있는 에러가 있습니다. 대표적으로 다음의 세가지 502, 503, 504 입니다. 이 중에서 이번에 알아볼 문제는 HTTP 504 에러입니다 .  타임 아웃이 되어   Request 를 처리하지 못하는 상황이 됩니다 .   해결 방법부터 이야기 하자면 다음과 같이 웹서버의 Time-out 시간을 60 초 이상으로 늘리는 것입니다 . Web Server & Application Time-out >= 60 sec 그 이유는 다음과 같은 ELB의 특성 때문입니다. ELB는 클라이언트와 EC2 서버 양쪽으로 커넥션을 유지하고 있습니다. ELB는 클라이언트와  EC2 서버간의 커넥션을 관리하는 역할을 맡고 있습니다. 그래서 유효한 커넥션만을 남겨놓습니다. 이를 위해서 Time-out 시간을 가지고 이 시간동안 데이터가 송수신되지 않으면 연결을 끊습니다.  기본적으로 Elastic Load Balancing는 두 연결 모두에 대해 Time-out(유휴 시간) 시간을 60초로 되어 있습니다. 그렇기 때문에 HTTP 또는 HTTPS를 사용할 경우 "KeppAlive" 옵션을 사용하여 커넥션을 재활용해야 합니다. 이 때  ELB 커넥션도 재사용되기 때문에 CPU 사용률을 줄일 수 있습니다. Browser Time-out Opera 11.11 120 sec IE 9 60 sec Chrome 13 300 sec FireFox 4 115 sec 서버 로직 중에서 60초 이상 실행될 수 있는 부분이 있는 경우 504 에러를 자주 볼 수 있을 것입니다. 문제 해결을 위해서는 Web Server는 물론  Tomcat 설정 또한 60초 이상으로 변경해주어야 합니다.

REST Web Service에서 Projection 기능 활용

REST Web Service에서 Projection을 이용한 웹 인터페이스 유연성 확보하기. Projection 기능이란?   프로젝터와 같이 실제하는 물체를 보고 싶은 형태로 볼 수 있게 도와주는 기능을 말한다. 좀 더 쉽게 말하면 실제하는 물체란 필름을 말하는 것이고, 보고 싶은 형태란 70인치 화면으로 보여주는 것을 말한다. 설정에 따라 30인치 화면으로 볼 수도 있다.   즉, Projection 기능이 있다면 상황에 맞는 화면 크기로 보여 줄 수 있는 것이다. [프로젝터] 웹 인터페이스 설계의 어려움   웹서버를 개발하는 개발자 입장에서 하나의 인터페이스라도 만드는 일은 쉽지 않다. 그렇기 때문에 웹 인터페이스의 개수를 줄이려고 노력하는 것이 일반적이다. 그러기 위해서는 좀 더 일반적인(General) 형태의 인터페이스로 설계하는 수밖에 없다. 그러나 아이러니하게도 사용자(인터페이스를 사용하는 다른 개발자) 입장에서는 이런 일반적인 인터페이스를 쓰기란 여간 어려운게 아니다. 클라이언트마다 필요한 데이터가 다르다.  REST 웹서비스에서는 리소스에 해당하는 데이터를 다음과 같이 JSON 형식의 Response로 전달해준다. 예를 들어 다음과 같이 상품 정보를 보내주는 RESR API가 있다고 해보자. GET /api/furniture/item/123334333 위의 REST API를 이용하여 다음과 같은 데이터를 받을 수 있다. { "item_id" : 1233343333 , "name" : "chair" , "category" : "furniture" , "price" : 56000 , "seller" : "Makers" , "description" : "Goo

Drools Fusion "Steam mode" Issue and Solution

ESP(Event Stream Processing) 센서, CCTV등 다양한 소스(Source)에서 들어오는 실시간 이벤트를 이벤트 스트림 이라고 한다. 그리고 이벤트 스트림을 받아서 이를 실시간으로 처리하는 것을 Event Stream Processing이라고 한다. 처리라는 것은 대부분 특정 이벤트에 대해서 사용자가 원하는 동작 또는 알람을 보내는 작업을 말한다. 이슈 발생 대용량 이벤트 처리 기술은 항상 퍼포먼스와 안정성이 중요하다. 지금은 서버에서 Drools Fusion 을 사용하고 있다. 이전 버전에서 Esper 를 사용하였으나 라이센스 문제로 인해서 Drools Fusion으로 옮겨왔다. 문제는 성능 테스트 중에 발생하였는데 메모리 leak 문제였다. “Always” 문제였기 때문에 문제점 파악은 쉽게 되었다. 테스트 방법 * 5대의 서버를 준비 * JMeter  _를 이용하여 throughput을 조절해가며 이벤트 발생 * 1대의 서버에 서버 App 실행 * JConsole 을 이용하여 JVM Heap 사이즈를 모니터링 결론 최종적으로 이벤트가 고속이든, 저속이든 Heap 사이즈가 늘어난다는 판단 분석 방법 JMap 을 이용하여 Memory Dump하였다. 그리고 메모리 덤프 분석툴인 MAT 를 이용하여 Dump 분석 결과를 확인 분석 결과 모든 이벤트 오브젝트가 Drools hashmap에 reference되어 있었다. 문제 해결 과정(How to solve) 우선은 내 코드들을 의심해봤다. 이벤트 수신, Pooling, 처리, 저장, DB Access 부분을 모두 훓어 보았다. 그러나 문제가 될 만한 부분은 찾지 못했다. 이건 뭐지? 내 상식 이상의 뭔가가 있나 한참을 고민했다. 결국은 소스 코드를 링크해서 디버깅을 해야하나, 소스 코드 분석을 해야하나 고민하면서 Drools 메뉴얼을 훓어 보다가 머리 속에서 스파크가 팍 튀었다. 다음은 Drools