Skip to content

[성능개선] Like 검색에서 Full Text 검색으로 쿼리 튜닝

YoonTaeMin edited this page Sep 15, 2023 · 2 revisions

배경

image

주말내집 서비스 중 커뮤니티 서비스에는 검색 기능이 있습니다. 검색어를 입력하면, 게시글의 제목과 내용에 대해 검색어를 포함하는지 여부를 확인하고 결과를 돌려줍니다. 게시글 작성 시, 에디터를 기반으로 이루어지기 때문에 게시글 내용에 대한 값에는 HTML 태그가 포함되어 있습니다. 또한, 글자수 제한이 없기에 varchar로 관리하기 어렵습니다. 이를 해결하고자 앞선 이슈 회고글에서 게시글 테이블의 내용 컬럼을 Text로 변경했음을 언급했습니다.

Text 타입인 내용에 대해 특정 키워드를 포함하는지를 LIKE 연산자로 검색하는 것은 데이터가 많아지고 내용의 길이가 커질수록 성능 상 이점이 없다고 판단했습니다. 따라서 Full Text 검색 방식으로 쿼리를 튜닝하기로 결정하였습니다.

환경.

  • SpringBoot 2.7.8
  • Kotlin
  • MySQL5

과정

  1. 인덱스 생성

Full Text 검색 방식을 사용하기 위해서는 인덱스를 설정해주어야 합니다.

ALTER TABLE board ADD FULLTEXT INDEX idx_board_title(title) WITH PARSER NGRAM;
ALTER TABLE board ADD FULLTEXT INDEX idx_board_content(content) WITH PARSER NGRAM;

원하는 테이블의 원하는 컬럼에 맞추어 해당 쿼리를 변경해서 사용하면 됩니다.

콘솔창에서 해당 쿼리를 실행하면, 인덱스가 성공적으로 생성됩니다.

이제 QueryDsl에서 match 함수를 사용할 수 있도록 설정을 커스터마이징 하는 작업을 해보겠습니다.

  1. Config 파일 생성 및 연결
spring.jpa.properties.hibernate.dialect: 클래스가 위치한 파일 경로

위의 설정 코드를 yml에 입력해주어야 합니다.

class MySQL5DialectCustom : MySQL57Dialect() {
    init {
        registerFunction(
            "match",
            SQLFunctionTemplate(StandardBasicTypes.INTEGER, "match(?1) against (?2 in boolean mode)")
        )
    }
}

프로젝트에서 사용하는 DB Driver에 맞추어 버전 설정을 해주고, 커스터마이징을 하시면 됩니다. 위의 설정이 의미하는 바는 match SQL 함수를 QueryDsl에서 사용하기 위한 것입니다.

StandartBasicTypes에 Double을 설정하면 오류가 발생하니 저와 같은 환경에서 시도하는 분이라면, Integer를 사용해주시기 바랍니다. ( 해당 부분이 오류가 나는 이유는 아직 파악하지 못했습니다. )

  1. 쿼리 로직 변경
    private fun searchWithKeyword(keyword: String?): BooleanExpression? {
        if(keyword == null) return null // (1)
        val titleBoolean = Expressions.numberTemplate(
            Integer::class.java,
            "function('match',{0},{1})", board.title, "+*$keyword*"
        ) // (2)
        val contentBoolean = Expressions.numberTemplate(
            Integer::class.java, "function('match',{0},{1})", board.content,
            "+*$keyword*"
        ) // (3)
        return titleBoolean.gt(0).or(contentBoolean.gt(0)) // (4)
    }

해당 조건 함수는 실제로 where() 함수에서 사용되고 있습니다.

(1) keyword는 클라이언트에서부터 넘어오는 검색 키워드입니다. 해당 키워드가 없다면, 검색을 할 필요가 없기에 null을 반환시킵니다.

(2) 검색어에 대한 조건 확인을 위한 변수입니다. 앞서 config 클래스에서 정의한 함수를 사용하는 부분입니다. numberTemplate()를 이용해, 결과값을 Integer로 반환 받습니다. 결과가 존재한다면 1이상의 값을 반환합니다. 이때 주의할 점은 keyword 앞뒤에 *를 붙여주어야 한다는 것입니다.

(3) 2번과 동일하나 검색 대상이 제목이 아닌 내용입니다.

(4) 결과값으로는 2번 혹은 3번에 대해 or() 연산자로 합하여 반환합니다.

결과

기존에 LIKE 검색 대비 빠른 응답 속도를 보장하고 있습니다. 데이터의 수를 100, 500, 1000, 5000 씩 늘려나가며 차츰 테스트를 진행할 예정입니다.

Clone this wiki locally