최근 커뮤니티 기능을 가진 서버를 개발하다보니, 게시글 검색 기능을 구현하게 되었다.
요구조건은 아래와 같다.
- 검색결과는 제목->내용->키워드 순으로 배열
- 제목, 내용, 키워드로 검색된 내용을 각각 최신순으로 나열
요구조건을 딱 보자마자 든 생각은 쿼리 수행시간이 얼마나 될까 하는 걱정이였다.
그래서 나중에 쿼리 수행시간을 테스트하기로 하고, 요구사항 대로 쿼리문을 짜보았다.
SELECT * FROM board
WHERE title rlike '테스트' AND title rlike '키워드'
ORDER BY t2.board_id DESC
UNION
SELECT * FROM board
WHERE content rlike '테스트' AND content rlike '키워드'
ORDER BY c2.board_id DESC
UNION
SELECT * FROM board
WHERE tag rlike '테스트' AND tag rlike '키워드'
ORDER BY k2.board_id DESC
LIMIT 1000;
각 서브 쿼리문을 최신순으로 정렬하고 UNION으로 결합했다.
하지만 원하는대로 결과가 나오지 않았다. 왜냐하면 MySQL은 서브쿼리 내의 정렬은 적용이 되지 않는다.
그래서 다음과 같이 변경하였다.
(SELECT * FROM board
WHERE
title rlike '테스트' AND title rlike '키워드'
ORDER BY board_id DESC LIMIT 1000)
UNION
(SELECT * FROM board
WHERE
content rlike '테스트' AND content rlike '키워드'
ORDER BY board_id DESC LIMIT 1000)
UNION
(SELECT * FROM board
WHERE
tag rlike '테스트' AND tag rlike '키워드'
ORDER BY board_id DESC LIMIT 1000)
LIMIT 1000;
이게 왜 먹히는지는 아래 사이트를 참고하자.
이제 원하는 대로 결과가 나왔다. 하지만 한 가지 문제가 남아있었다. 성능...
REGEXP를 사용하거나 LIKE에 검색어 앞에 '%'를 넣어 검색할 경우, 인덱스를 타지 않고 풀 스캔(Full Scan)이 일어나게 된다.
그럼 이제 인덱스를 사용할 수 있도록 쿼리문을 바꿔보자.
하지만 그 전에 먼저 인덱스를 설정해야 한다.
alter table board add FULLTEXT(title);
alter table board add FULLTEXT(content);
alter table board add FULLTEXT(tag);
그리고 테스트로 다음의 쿼리문을 실행해보았다.
SELECT * FROM db_test.board WHERE match(title) against('제목');
하지만 아무 결과도 나오지 않았다.
그래서 구글링을 해보니, FULLTEXT 검색 엔진의 최소 검색어 길이 값이 innoDB의 경우 3인 것을 알게 되었다.
최소 검색어 길이를 2로 설정해주자.
내 경우엔, AWS의 RDB를 이용하기 때문에 다음과 같이 RDB 파라미터 그룹에서 해당 값을 설정해주었다.
설정을 완료한 뒤 MySQL을 재부팅하고, 인덱스도 다시 설정해주어야 적용이 된다.
위의 테스트 쿼리를 날려보니, 이제 검색이 된다.
모든 준비는 끝났다. 이제 쿼리문을 바꿔보자.
(SELECT * FROM board
WHERE
match(title) AGAINST('+"테스트" +"키워드"' in boolean mode)
ORDER BY board_id DESC LIMIT 1000)
UNION
(SELECT * FROM board
WHERE
match(content) AGAINST('+"테스트" +"키워드"' in boolean mode)
ORDER BY board_id DESC LIMIT 1000)
UNION
(SELECT * FROM board
WHERE
match(tag) AGAINST('+"테스트" +"키워드"' in boolean mode)
ORDER BY board_id DESC LIMIT 1000)
LIMIT 1000;
REGEXP를 모두 FULLTEXT 검색으로 바꿔주었다.
이제 대략 60000개의 행을 추가한 뒤, 두 쿼리문의 실행시간을 비교해보았다.
REGEXP | FULLTEXT | |
Duration | 0.21340025 | 0.68861400 |
이럴수가...
반전의 결과가 나왔다. 인덱스까지 사용했는데 왜 FULLTEXT 검색이 더 느릴까?
알고보니, 테스트용으로 같은 텍스트 값을 몇 만 개를 넣었기 때문에 Index가 소용이 없었던 것이였다.
MySQL 샘플 데이터베이스로 비교해보니 FULLTEXT 검색이 훨씬 빠른 것을 확인했다.
SELECT * FROM world.city
WHERE match(Name) AGAINST('seoul' in boolean mode);
SELECT * FROM world.city
WHERE Name rlike 'seoul';
REGEXP | FULLTEXT | |
Duration | 0.0055 | 0.0019 |
참고 사이트
Mysql Mariadb full-text 풀텍스트 검색 방법 자세히 알아보기
MySQL / MariaDB 서브 쿼리 내의 order by 안 되는 현상 + 해결법
'Database' 카테고리의 다른 글
데이터베이스 설계 시 테이블 id INT or BIGINT? (0) | 2021.12.16 |
---|---|
MySQL 기초 2 (0) | 2021.11.10 |
MySQL 기초 1 (0) | 2021.11.10 |