MySQL의 Full-Text Search 기능 사용법

FULLTEXT 인덱스를 활용하면 제목·본문처럼 긴 텍스트에서 단어 기반 랭킹 검색을 빠르게 수행할 수 있습니다.

실무에서 바로 쓰는 명령과 설정 중심으로, InnoDB + 한국어 환경까지 한 번에 정리합니다.

 

1) FULLTEXT 한눈에 보기

  • 지원 엔진: InnoDB(권장), MyISAM
  • 대상 컬럼: CHAR/VARCHAR/TEXT 계열
  • 질의 모드: NATURAL LANGUAGE, BOOLEAN, WITH QUERY EXPANSION
  • 핵심 함수: MATCH(col[, ...]) AGAINST('query' [MODE])
-- 기본 문법
SELECT id, title,
       MATCH(title, body) AGAINST ('mysql indexing') AS score
FROM articles
WHERE MATCH(title, body) AGAINST ('mysql indexing' IN NATURAL LANGUAGE MODE)
ORDER BY score DESC;

: 점수(score)를 SELECT에 노출하고 ORDER BY로 내림차순 정렬하면 관련도 높은 문서가 먼저 나옵니다.

 

 

2) FULLTEXT 인덱스 생성/관리

-- 테이블과 동시에 생성
CREATE TABLE articles (
  id      INT PRIMARY KEY AUTO_INCREMENT,
  title   VARCHAR(200),
  body    TEXT,
  FULLTEXT KEY ft_title_body (title, body) -- 다중 컬럼 가능
) ENGINE=InnoDB;

-- 기존 테이블에 추가
CREATE FULLTEXT INDEX ft_title_body ON articles (title, body);

-- 삭제
DROP INDEX ft_title_body ON articles;

언제 쓰나? LIKE '%키워드%'로는 대용량에서 느립니다. FULLTEXT토큰화+역색인 기반이라 훨씬 효율적입니다.

 

 

3) 질의 모드별 사용법

3-1. NATURAL LANGUAGE MODE

자연어 점수화. 불용어·최소 토큰 길이 규칙을 따르고, 자동 랭킹을 제공합니다.

SELECT *, MATCH(title, body) AGAINST ('mysql fulltext') AS score
FROM articles
WHERE MATCH(title, body) AGAINST ('mysql fulltext' IN NATURAL LANGUAGE MODE)
ORDER BY score DESC;

3-2. BOOLEAN MODE (정밀 제어)

  • + 반드시 포함, - 제외
  • "문구" 정확한 구
  • * 접미 와일드카드(접두 미지원): optimiz*
  • >/< 점수 가중치 상향/하향, ~ 페널티
SELECT id, title
FROM articles
WHERE MATCH(title, body)
      AGAINST ('+"full text" +mysql -postgres optimiz* >index' IN BOOLEAN MODE);

3-3. WITH QUERY EXPANSION (의미 확장)

초기 결과에서 연관 단어를 재학습해 재검색합니다(재랭킹). 과적합을 피하려면 정확 질의에만 제한적으로 사용하세요.

SELECT id, title
FROM articles
WHERE MATCH(title, body)
      AGAINST ('database scaling' WITH QUERY EXPANSION)
ORDER BY MATCH(title, body) AGAINST ('database scaling' WITH QUERY EXPANSION) DESC;

 

4) 한국어 검색 품질 높이기: ngram 파서

공백 기준 토큰화가 어려운 한국어는 ngram 파서를 쓰면 성능과 적중률이 좋아집니다.

-- ngram 파서 사용(서버에 내장)
CREATE FULLTEXT INDEX ft_title_body
ON articles (title, body)
WITH PARSER ngram;

-- 토큰 길이(기본 2)를 2~10 사이로 조정 (재시작 필요할 수 있음)
SET PERSIST ngram_token_size = 2;

실무 팁

  • 짧은 토큰(2~3글자)이 검색 재현율을 높이고, 긴 토큰은 정밀도를 올립니다. 서비스 성격에 맞춰 튜닝하세요.
  • 불용어·최소 길이 규칙(아래 6장 참고)도 CJK에 영향—값을 낮추면 더 많은 단어가 색인됩니다.

 

5) 랭킹, 하이라이트, 페이징

-- 관련도 점수로 정렬 + 페이징
SELECT id, title, 
       MATCH(title, body) AGAINST(:q IN NATURAL LANGUAGE MODE) AS score
FROM articles
WHERE MATCH(title, body) AGAINST(:q IN NATURAL LANGUAGE MODE)
ORDER BY score DESC
LIMIT :offset, :limit;

하이라이트는 SQL 내장 기능이 없습니다. 애플리케이션에서 질의어 토큰을 기준으로 표시 문자열에 마크업을 입히세요(예: <mark>…</mark>).

 

 

6) 토큰 길이·불용어·서버 설정

  • 최소 토큰 길이: InnoDB는 innodb_ft_min_token_size (기본 3), MyISAM은 ft_min_word_len (기본 4)
  • 불용어: innodb_ft_server_stopword_table 로 커스텀 테이블 지정 가능
  • 설정 변경 후에는 인덱스 재생성이 필요합니다.
-- 예: 2글자부터 색인하고 커스텀 불용어 사용
SET PERSIST innodb_ft_min_token_size = 2;
CREATE TABLE stopwords (value VARCHAR(18)) ENGINE=InnoDB;
INSERT INTO stopwords(value) VALUES ('그리고'), ('하지만');
SET PERSIST innodb_ft_server_stopword_table = 'mydb/stopwords';

-- 인덱스 재빌드(드롭 후 재생성 또는 OPTIMIZE)
ALTER TABLE articles DROP INDEX ft_title_body,
                     ADD FULLTEXT INDEX ft_title_body (title, body) WITH PARSER ngram;

 

7) 성능 & 운영 체크리스트

  • 쓰기 비용: FULLTEXT는 쓰기 시 색인 갱신 비용이 큽니다. 대량 적재는 인덱스 생성 전에 하고, 끝나면 인덱스를 추가하세요.
  • 조건 혼합: 범위/필터는 WHERE로 먼저 줄이고, 마지막에 MATCH AGAINST로 랭킹하세요.
  • EXPLAIN으로 type=fulltext 사용을 확인해 불필요한 테이블 스캔을 피합니다.
  • 백업/복구: 덤프·복원 시 FULLTEXT 메타는 자동 재구성되지만, 서버 변수 변경 후엔 재색인이 필요할 수 있습니다.
  • 대체 전략: 복잡한 형태소 분석·하이라이트·집계가 중요하면 Elasticsearch/OpenSearch와의 하이브리드도 검토하세요.

 

8) 자주 쓰는 쿼리 스니펫

-- 1) boolean MUST(+), MUST NOT(-), exact phrase
SELECT id, title
FROM articles
WHERE MATCH(title, body)
      AGAINST ('+"인덱스 최적화" -postgres mysql*' IN BOOLEAN MODE);

-- 2) 다중 컬럼 가중치(간단 가중치 합산)
SELECT id, title,
       ( MATCH(title) AGAINST(:q IN NATURAL LANGUAGE MODE) * 2
       + MATCH(body)  AGAINST(:q IN NATURAL LANGUAGE MODE) ) AS score
FROM articles
WHERE MATCH(title, body) AGAINST(:q IN NATURAL LANGUAGE MODE)
ORDER BY score DESC;

-- 3) 날짜 필터 + 전문 검색
SELECT id, title, publish_at
FROM articles
WHERE publish_at >= CURDATE() - INTERVAL 30 DAY
  AND MATCH(title, body) AGAINST(:q IN NATURAL LANGUAGE MODE)
ORDER BY MATCH(title, body) AGAINST(:q IN NATURAL LANGUAGE MODE) DESC;

 

 

 

MySQL FULL-TEXT Search는 “빠른 랭킹 검색”이 필요한 서비스에 강력한 기본기능을 제공합니다. 인덱스 설계(다중 컬럼, 가중치), 모드 선택(NATURAL/BOOLEAN), 한국어는 ngram까지 갖추면, 별도 검색 서버 없이도 충분한 품질을 낼 수 있습니다. 남은 과제는 데이터와 트래픽에 맞춘 토큰 크기·불용어·재색인 전략 튜닝입니다.