엘라스틱 서치를 이용한 검색 기능 성능 개선 (1)

Hyuk·2023년 10월 27일
0

HappyScrolls 개발기

목록 보기
23/24

기존 검색 기능의 구현

검색 기능은 다음과 같이 구현되어 있습니다.

    @Override
    public List<Article> search(Long lastindex, Integer limit, String param) {
        return jpaQueryFactory
                .selectFrom(article)
                .where(article.title.contains(param),article.id.gt(lastindex))
                .limit(limit)
                .fetch();
    }

위 코드는 mysql로 치면

where article.title like %param% and article.id > listindex

와 같은 쿼리가 실행됩니다.

뒤쪽에 있는 article.id > listindex 부분은 offset을 사용하지 않는 페이징을 위해 적용한 것입니다.

검색을 위한 부분은 like %param% 인데, title에서 param의 내용이 포함된 모든 항목을 감색해서 반환합니다.

문제점

like 쿼리에서 %가 앞쪽에 있는 like %param 과 같은 쿼리는 인덱스를 사용할 수 있습니다.

하지만 like %param%과 같이 검색하고자 하는 내용이 첫 부분이 아니라 포함되어 있는 경우라면, 인덱스를 타지 않고 풀스캔을 하게 됩니다.

실제로 기능을 호출해보면 5초가 넘는 실행시간이 걸리게 됩니다.
현재 전체 데이터는 1000만건 가량 존재하는 상태입니다.

검색 한 번에 5초면 도무지 사용할 수 없는 기능이기에, 개선해보고자 했습니다.

선택지

선택지는 두 가지가 있다고 생각되었습니다.

1. Mysql full text search

MySQL의 전문 검색 기능은 MyISAM 및 InnoDB 스토리지 엔진에서 텍스트 필드에서 문자열을 빠르게 검색할 수 있게 해줍니다.

2. Elastic Search

Elasticsearch는 실시간 분산 검색 및 분석 엔진입니다. Lucene 라이브러리를 기반으로 하며, JSON 형식으로 데이터를 저장하고 RESTful API로 검색과 집계를 수행합니다

두 선택지 중에서 Elastic Search를 선택하였습니다.

첫번째 이유는 기능의 확장성 때문입니다. 엘라스틱 서치를 사용하면 Mysql full text search를 선택했을 때와 비교해서 보다 복잡한 검색도 가능하다는 이유가 눈길을 끌었습니다.

두번째 이유는 분산입니다. 현재 Mysql도 클러스터 환경으로 구축되어 있지만, 검색과 관련된 기능을 Elastic Search에 맡긴다면, Mysql이 부담해야 할 부하가 줄어들 것으로 기대되기 때문입니다.

과정

도커에 ELK 스택을 구축하고, Logstash를 통해 Mysql에 저장된 데이터를 엘라스틱 서치에도 저장될 수 있도록 하였습니다.

엘라스틱 서치를 스프링부트 프로젝트에서 사용하는 것은 생각보다 간단했습니다. 스프링부트가 엘라스틱 서치를 지원하기 때문에, Spring Data Jpa를 사용하는 것과 비슷한 방법으로 사용할 수 있었습니다.

public interface ArticleDocRepository extends ElasticsearchRepository<ArticleDoc, Long>, CrudRepository<ArticleDoc, Long> {
    List<ArticleDoc> findAllByTitle(String title);

    List<ArticleDoc> findAllByTitleContaining(String title);

    Page<ArticleDoc> findAllByTitleContaining(String title,Pageable pageable);
}

로그를 통해서 findAllByTitleContaining 메소드를 호출하면 다음과 같이 엘라스틱 서치로 요청이 가게 되는 것을 알 수 있습니다.

 request [POST http://localhost:9200/articletest/_search?typed_keys=true&max_concurrent_shard_requests=5&search_type=query_then_fetch&batched_reduce_size=512] 

그리고 다음과 같은 결과를 반환받은 것도 확인할 수 있습니다.

returned [HTTP/1.1 200 OK]

결과

엘라스틱 서치로 구현한 기능과 기존 Mysql의 like 쿼리를 QueryDsl로 구현한 기능은 같은 결과를 반환합니다.

하지만 엘라스틱 서치로 구현한 기능은 다음과 같이 뛰어난 응답시간을 가지게 됩니다.

10회의 실행시간을 평균내었을 때
Mysql like를 사용했을 때의 평균 시간은 3502ms이고,
엘라스틱 서치를 이용했을 때에는 212ms가 소모되었습니다.

적재되어 있는 데이터의 상황과, 검색하고자 하는 조건에 따라 성능의 차이가 발생하겠지만, 이 경우에는 16.5배 가량의 성능 향상을 경험할 수 있었습니다.

profile
🙂 🙃 🙂 🙃

0개의 댓글