case study

버즈니 ES 검색엔진 마이그레이션 - ES(Lucene) 핵심 변화 리서치

버즈니는 최근에 ES 2.3.3에서 7.8.1 버전으로의 마이그레이션을 진행했다. 결과적으로 버즈니에서는 검색 응답 속도 p99 지표가 4초 → 0.7초로 약 6배 향상됐다. ES의 버전이 올라가는 과정에서 내부적으로 도대체 어떤 변화들이 있었던 걸까? ES 마이그레이션 과정에서 버즈니가 진행한 리서치 내용들을 공유한다.

2021.3.18(목)
버즈니 ES 검색엔진 마이그레이션 - ES(Lucene) 핵심 변화 리서치

읽어보면 좋을 독자

→ 검색엔진으로 ES7 미만의 버전을 사용하고 있다.

→ ES7에서 무언가 큰 개선이 있었다고 하는데, 자세히 알고싶다.


ES 마이그레이션 해도될까?

마이그레이션이 개발자 입장에서 쉽게 내릴 수 있는 간단한 결정은 아니다. 버즈니의 사례에서도 마이그레이션 과정에서 예상하지 못한 크고 작은 버그가 발생했고, 셀 수 없이 많은 개선 과정이 필요했다. 색인 로직 변화를 제외한 검색 관련 코드 변화만 최소 2000 Line 이상이 변경됐으며, 과정의 장애물을 어떻게 관리해야 하는지에 대한 방법들을 많이 고민했다. 


결과적으로 ES7로 마이그레이션을 진행했고, 검색 성능은 대폭 향상 됐는데, [회고] 버즈니에서 검색 백엔드 엔지니어로 사는 이야기에서 볼 수 있듯이 버즈니는 주도적으로 생각한 아이디어들을 시도해볼 수 있는 분위기를 갖고있었고, 매 순간 합리적인 의사 결정이 가능했다. 덕분에 중간중간 발생한 문제들은 큰 어려움 없이 해결할 수 있었다.


버즈니 검색 서비스 ES 마이그레이션 과정


ES(Lucene)에 일어난 최근 3년의 변화

장애물을 무릅쓰고도 마이그레이션을 해야만 했던 이유는 뭘까? 결과적으로 얘기하면, 대폭적인 성능 개선이 가능하다. 최근 2~3년 사이 오픈소스 ES(Lucene) 커뮤니티에는 많은 혁신의 바람이 불고 있다. 


대표적으로 Block-Max-WAND는 Top-K term query 검색 성능을 약 3~7배 개선시켰고, 색인 동시성 개선은 색인 성능의 7~8배 개선을 만들었다. 그리고 FST offheap을 통한 메모리 사용량 개선, rank_feature 기능 추가, Indexing custom term frequencies 도입등의 주요 변화들이 있었다. 


이런 변화는 Lucene 커뮤니티에 자주 있어왔던 일이 아니다. 유래없는 변화들이 최근 3년 사이에 일어났고, 그 결과를 ES7 에서 볼 수 있다.


아마존이 내부 검색엔진으로 Lucene 사용을 결정하다

이 변화의 배경에 있는 중요한 사실은, 2019년 아마존이 내부 검색엔진으로 Lucene을 전격적으로 사용하기 시작했다는 사실이 있다.


아마존은 2017년 오픈소스 Lucene의 핵심 개발자이며 Lucene in Action의 저자인 McCandless를 영입했다. 그리고 최근에 아마존의 천재 개발자이며, Lucene 커뮤니티의 PMC Chair를 맡게된 Michael Sokolov는 ANN(Approximate nearest vector search) 기능을 Lucene에 추가하기 위해 위해 고군분투 했다. 


세계 최고의 커머스 회사인 아마존이 내부 검색엔진으로 Lucene을 선택했고, Lucene의 핵심 엔지니어들을 영입했다. 그리고 지금 이 순간 다양한 기여를 만들고 있다. 최신의 ES(Lucene)를 적극 활용할 수 있다면, 우리는 승차감 좋은 아마존 버스에 탑승할 수 있다. follow 하지 않을 이유가 있을까? 


만일 기술부채로 오랫동안 방치되어 있는 ES가 눈앞에 보인다면, 혁신적인 변화를 만들어내기에 좋은 기회라고 생각한다.


ES (Lucene)의 핵심 변화


Block-Max-WAND (LUCENE-8135, Adrien Grand, @Elastic)

우리가 많이 사용하는 Should query는 Must query에 비해 score 계산 대상의 수가 월등히 많다. 이는 검색 성능 저하의 큰 원인이 된다. 


Adrien Grand는 2012년의 MAXSCORE, 2017년의 WAND를 지나 결국 2019년에 Block-Max-WAND 기능을 추가했다. 이 기능으로 인해, 계산이 필요하지 않은 검색 대상들을 빠르게 건너뛸 수 있게 되었고, Top-k 검색의 속도가 매우 크게 개선됐다. 


만약 서비스 요구사항에서 전체 상품 수를 계산할 필요가 없고 랭킹 스크립트등으로 custom하게 score를 변경하는 기능을 사용하고 있지 않다면, Block-Max-WAND 기능은 검색 성능 개선에 매우 큰 도움이 될 수 있다.


FeatureField(rank_feature) (LUCENE-8197, Adrien Grand, @Elastic)

‘좋아요’가 많이 달린 글의 검색 랭킹을 높이고 싶은 경우, 좋아요 field에 색인된 값을 점수 계산에 활용할 수 있다. 이 요구사항을 위해 ES는 랭킹 스크립트를 제공해왔는데, 랭킹 스크립트는 스크립트 엔진이 모든 대상 문서를 직접 순회하며 score를 계산하는 방식이다. 따라서 계산 비용이 매우 비싸고, 성능 최적화가 어렵다. 


버즈니의 사례에서는 과거 ES2의 groovy ranking script가 매우 큰 성능 병목의 원인중 하나였고, painless로 개선되면서 그나마 성능이 많이 개선됐다. 그런데, rank_feature 기능 추가로 인해, 랭킹 스크립트가 없이 field에 색인된 값을 점수 계산에 활용할 수 있게 됐다. 게다가 이 기능은 Block-Max-WAND 최적화에 긍정적인 영향을 받는다.


Lazy loading Lucene FST offheap using mmap (LUCENE-8635, Ankit Jain, @Amazon)

Lucene에서 FST는 힙메모리에서 관리돼 왔다. 아마존에서 일하고 있는 Ankit Jain은 FST를 memory-mapped IO로 lazy load 할 수 있도록 개선을 시도했다. 이 개선으로 핫 인덱스에서 성능 저하 없이 메모리 사용량이 크게 감소했다. Elastic의 10TB 인덱스 테스트 벤치마크에서 기존 17GB였던 메모리 사용량이 2.5GB로 약 7배 가까이 개선된 결과를 보여줬다.


Indexing custom term frequencies (LUCENE-7854, Michael McCandless, @Amazon)

Lucene의 핵심 개발자 Michael McCandless가 아마존에 입사하고 바로 개선한 이슈인데, 수정 내용을 보면 굉장히 인상적이다. Lucene 검색엔진은 내부적으로 검색 score의 계산을 위해 TF(term frequencies - 검색 단어의 등장 횟수)를 활용한다. 


그런데, 이번 수정에서 term frequency 값을 색인 시점에 custom하게 임의의 값(vector)을 입력할 수 있도록 변경했다(이로 인해, 결과적으로 TermFrequency는 더이상 "단어의 등장 횟수: term frequency"가 아니게 되었다..!). 이 변화를 기반으로 앞서 보았던 FeatureField, vector search등의 기능 추가가 이뤄졌고, 머신러닝 학습된 모델을 Lucene에서 사용할때 활용할 수 있는 획기적인 초석이 됐다.


Use multiple threads to apply deletes and DV updates (LUCENE-7868, Michael McCandless, @Amazon)

이 이슈 역시 아마존의 Michael McCandless가 개선했다. 기존의 Lucene에서 문서를 삭제하거나, 문서의 값을 업데이트 할때 큰 병목이 발생했었다. 단순히 새로운 글을 추가하는 로깅의 용도에서는 문제가 없지만, 지속적으로 문서를 업데이트를 해야 하는 경우에는 이 부분이 주요 병목 포인트였다. 


특히, 아마존과 버즈니 같이 상품을 색인해야 하는 커머스 서비스의 경우, 상품의 변경이 빈번하기 때문에 이 병목에 영향이 크다. Michael McCandless는 이 문제를 해결하기 위해 delete와 doc value update가 multiple threads로 이루어지도록 개선했고, doc values 업데이트 속도가 7~8배로 대폭 향상됐다.


Approximate nearest vector search (Lucene-9004, Michael Sokolov, @Amazon)

아마존은 ES(Lucene)에 ANN을 효과적으로 적용하기 위해 노력하고 있고, Michael Sokolov라는 개발자가 핵심적인 기여를 하고 있다. 다양한 ANN 알고리즘 중 state-of-art로 알려진 nmslib의 HNSW(Hierarchical Navigable Small World graphs)를 Lucene에 적용하는 일을 최근에 진행했고, 이 기능은 Lucene 9.0에 들어갈 예정이다. 


ES와 Lucene를 사용하고 있는 다양한 영역에서 ANN이 추가되는 것을 기대하고 있다. 아마존이 앞으로 ANN을 통해 검색 서비스에 어떤 변화들을 만들어 나갈지도 기대되는 부분이다.


 버즈니는 이미지 검색 등 vector space search에서 nmslib(HNSW)를 사용하고 있다. nmslib(HNSW)에는 문서 삭제, 결과 필터링, 가중치 계산 등 해결이 쉽지 않은 다양한 한계가 여전히 존재하는데, Michael Sokolov은 Lucene에 도입하면서 그런 부분들도 해결하기 위해 노력하고 있다.


관련문서

버즈니
홈쇼핑모아
스타트업
백엔드 엔지니어
검색 서비스
검색엔진
ES7
ES 마이그레이션
buzzni
pr@buzzni.com