CS/데이터베이스

MySQL Full-Text Search

duddn 2024. 3. 8. 01:02

made by gpt

왜 사용해야 할까요?

데이터베이스에서 특정 텍스트를 검색할 때, 우리는 %LIKE% 구문을 사용할 수 있습니다. 예를 들어, 사용자가 입력한 키워드를 포함하는 모든 레코드를 찾고 싶을 때 이 방법이 유용할 수 있습니다. 하지만 이 방법의 문제점은 테이블 풀 스캔을 피할 수 없다는 것입니다. 데이터가 많아질수록 검색 속도가 점점 느려지고, 서버에 부담을 줄 수 있습니다.

이런 문제를 해결하기 위해 MySQL은 전문검색을 위한 인덱싱 기능을 제공합니다. 이 기능을 사용하면 검색 속도를 크게 향상시킬 수 있습니다.

어떻게 사용해야하는 건가요?

MySQL에서 전문검색을 사용하기 위해서는 먼저 FULLTEXT 인덱스를 생성해야 합니다.

CREATE TABLE articles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(100),
    content TEXT,
    FULLTEXT(title, content)
);

여기서 FULLTEXT(title, content)는 titlecontent 필드에 전문검색 인덱스를 생성하라는 의미입니다.

한국어의 경우, 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%를 사용하고 있는경우 전문검색 기능을 고려해볼만 하다고 생각합니다.