MySQL Full-Text Search
왜 사용해야 할까요?
데이터베이스에서 특정 텍스트를 검색할 때, 우리는 %LIKE% 구문을 사용할 수 있습니다. 예를 들어, 사용자가 입력한 키워드를 포함하는 모든 레코드를 찾고 싶을 때 이 방법이 유용할 수 있습니다. 하지만 이 방법의 문제점은 테이블 풀 스캔을 피할 수 없다는 것입니다. 데이터가 많아질수록 검색 속도가 점점 느려지고, 서버에 부담을 줄 수 있습니다.
이런 문제를 해결하기 위해 MySQL은 전문검색을 위한 인덱싱 기능을 제공합니다. 이 기능을 사용하면 검색 속도를 크게 향상시킬 수 있습니다.
어떻게 사용해야하는 건가요?
MySQL에서 전문검색을 사용하기 위해서는 먼저 FULLTEXT 인덱스를 생성해야 합니다.
CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(100),
content TEXT,
FULLTEXT(title, content)
);
여기서 FULLTEXT(title, content)는 title과 content 필드에 전문검색 인덱스를 생성하라는 의미입니다.
한국어의 경우, WITH PARSER ngram 옵션을 추가하여 N-gram 파서를 사용하는게 유리합니다.
CREATE TABLE articles_korean (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(100),
content TEXT,
FULLTEXT(title, content) WITH PARSER ngram
);
N-GRAM
영어의 경우
공백 기반 파싱(Whitespace Tokenizer):
- 문장: "Hello World"
- 파싱 결과: ["Hello", "World"]
N-gram 파싱(2-gram 예시):
- 문장: "Hello World"
- 파싱 결과: ["He", "el", "ll", "lo", "o ", " W", "Wo", "or", "rl", "ld"]
한국어의 경우
공백 기반 파싱(Whitespace Tokenizer):
- 문장: "안녕하세요 세계"
- 파싱 결과: ["안녕하세요", "세계"]
N-gram 파싱(2-gram 예시):
- 문장: "안녕하세요 세계"
- 파싱 결과: ["안녕", "녕하", "하세”, “세요", "세계"]
WITH PARSER ngram을 사용하지 않으면 MySQL은 기본적으로 공백을 기준으로 단어를 파싱합니다. 이는 영어와 같은 라틴 기반 언어에서는 잘 작동하지만, 한국어와 같은 언어에서는 문제가 될 수 있습니다. 왜냐하면 한국어는 공백이 아닌 음절이나 자음, 모음 단위로 구분되기 때문입니다. 공백 기반 파싱을 사용할 시 “안녕”을 검색할 수 없습니다.
N-gram은 이러한 언어 특성을 고려하여 텍스트를 더 세밀하게 분석할 수 있게 해줍니다. 예를 들어, 2-gram(bigram)은 "안녕하세요"를 "안녕", "녕하", "하세”, “세요"와 같이 두 글자씩 끊어서 인덱싱합니다.
주의사항으로 위의 2-gram에서 2글자 단위로 처리하므로 1글자 검색이 불가능합니다. 1글자 검색을 지원하고 싶으면 다음과 같이 설정해야합니다.
SET GLOBAL ngram_token_size = 1;
자연어 모드와 불린모드
MySQL의 전문검색에는 자연어 모드와 불린 모드 두 가지가 있습니다. 자연어 모드는 검색어의 일치점수를 포함해 반환합니다. 반면, 불린 모드는 검색어에 불린 연산자(+, -, *, <, >)를 사용하여 보다 정교한 검색을 할 수 있습니다. 예를 들어, +MySQL -Oracle는 "MySQL"은 포함되고 "Oracle"은 포함되지 않는 문서를 찾습니다.
- 자연어 모드
SELECT
title,
MATCH(title, content) AGAINST('MySQL tutorial'
IN NATURAL LANGUAGE MODE) AS score
FROM articles
ORDER BY score DESC;
+---------------------------+---------------------+
| title | score |
+---------------------------+---------------------+
| MySQL Tutorial | 2.1748573780059814 |
| Introduction to Databases | 0.45309528708457947 |
| Oracle Guide | 0 |
| Oracle Database Guide | 0 |
+---------------------------+---------------------+
- 불린모드
SELECT
title,
MATCH(title, content) AGAINST('+MySQL -Oracle'
IN BOOLEAN MODE) AS score
FROM articles;
+---------------------------+--------------------+
| title | score |
+---------------------------+--------------------+
| MySQL Tutorial | 0.7249524593353271 |
| Oracle Guide | 0 |
| Introduction to Databases | 0 |
| Oracle Database Guide | 0 |
+---------------------------+--------------------+
불용어(Stopwords)
불용어는 검색 시 무시하고 싶은 단어입니다. 예를 들어, "the", "is", "at"과 같은 영어 단어나 "이", "그", "저"와 같은 한국어 단어가 불용어입니다. 이러한 불용어는 문서의 의미를 구성하는 데 큰 역할을 하지 않기 때문에, 전문검색에서는 이들을 제외하고 인덱싱하는 것이 일반적입니다.
MySQL에서는 INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD 테이블을 통해 기본 불용어 목록을 확인할 수 있고, 사용자 정의 불용어 목록을 만들어 사용할 수도 있습니다. 불용어를 적절히 관리하면 검색 효율을 높이고 저장 공간을 절약할 수 있습니다.
CREATE TABLE my_stopwords (
value VARCHAR(30)
);
INSERT INTO my_stopwords (value) VALUES ('이'), ('그'), ('저');
CREATE FULLTEXT INDEX ft_index ON articles (title, content)
WITH PARSER ngram
STOPWORD TABLE my_stopwords;
언제 사용해야 할까요?
전문검색에는 여러 대안이 있습니다. 엘라스틱서치와 같은 도구는 더 뛰어난 성능과 기능을 제공하지만, 러닝커브가 높고 설정이 복잡합니다. 또 소규모 데이터셋에서는 성능이 중요하지 않을 수 있습니다.
만약 MySQL을 사용하는 프로젝트에서 소규모의 데이터셋을 다루고 있다면, 간단하게 전문검색을 구현하고 싶거나 %LIKE%를 사용하고 있는경우 전문검색 기능을 고려해볼만 하다고 생각합니다.