전문 검색 인덱스

공부하는 감자·2024년 3월 7일
0

MySQL

목록 보기
13/74
post-thumbnail

전문 검색 인덱스

  • 앞서 정리한 인덱스 알고리즘은 일반적으로 크지 않은 데이터 또는 이미 키워드화한 작은 값에 대한 인덱싱 알고리즘이다.
    • MySQL의 B-Tree 인덱스는 실제 칼럼의 값이 1MB더라도 1000바이트(MyISAM) 또는 3072바이트(InnoDB)까지만 잘라서 인덱스 키로 사용한다.
  • 전문 검색 인덱스는 문서 전체에 대한 분석과 검색을 위한 인덱스 알고리즘이다.
    • 전문(Full Text) 검색은 문서 내용 전체를 인덱스화해서 특정 키워드가 포함된 문서를 검색하는 것이다.
    • 전문 검색 인덱스는 일반화된 기능의 명칭이지 전문 검색 알고리즘의 이름을 지칭하는 것이 아니다.

인덱스 알고리즘

  • 전문 검색에서는 문서 본문의 내용에서 사용자가 검색하게 될 키워드를 분석해 내고, 빠른 검색용으로 사용할 수 있게 이러한 키워드로 인덱스를 구축한다.
  • 전문 검색 인덱스는 문서의 키워드를 인덱싱하는 기법에 따라 크게 두 가지로 구분할 수 있다.
    • 단어의 어근 분석
    • n-gram 분석 알고리즘

어근 분석 알고리즘

  • MySQL 서버의 전문 검색 인덱스는 다음 과정을 거쳐 색인 작업이 수행된다.
    • 불용어(Stop Word) 처리
    • 어근 분석(Stremming)

불용어 처리

  • 검색에서 별 가치가 없는 단어를 모두 필터링해서 제거하는 작업
  • 불용어의 개수는 많지 않아서 다음처럼 사용한다.
    • 알고리즘을 구현한 코드에 모두 상수로 정의해서 사용한 경우가 많다.
    • 유연성을 위해 불용어 자체를 데이터베이스화해서 사용자가 추가하거나 삭제할 수 있게 구현하는 경우
  • 현재 MySQL 서버는 불용어가 소스코드에 정의되어 있다.
    • 이를 무시하고 사용자가 별도로 불용어를 정의할 수 있는 기능도 제공한다.

어근 분석

  • 검색어로 선정된 단어의 뿌리인 원형을 찾는 작업
  • MySQL 서버에서는 오픈소스 형태소 분석 라이브러리인 MeCab을 플러그인 형태로 사용할 수 있게 지원한다.
    • 각 국가의 언어가 서로 문법이 다르고 다른 방식으로 발전해왔기 때문에 형태소 분석이나 어근 분석 또한 언어별로 방식이 모두 다르다.
    • MeCab은 일본어를 위한 형태소 분석 프로그램이며, 서구권 언어를 위한 형태소 분석기는 MongoDB에서 사용되는 Snowball이 있다.
    • 한글이나 일본어의 경우 영어와 같이 단어의 변형 자체는 거의 없기 때문에 어근 분석보다는 문장의 형태소를 분석해서 명사와 조사를 구분하는 기능이 더 중요한 편이다.
  • MeCab을 위한 형태소 분석은 매우 전문적인 전문 검색 알고리즘이어서 만족할 만한 결과를 내기 위해서는 많은 노력과 시간을 필요로 한다.
    • 전문적인 검색 엔진을 고려하는 것이 아니라면 범용적으로 적용하기는 쉽지 않다.

n-gram 알고리즘

  • 단순히 키워드를 검색해내기 위한 인덱싱 알고리즘이다.
    • MeCab의 단점을 보완하기 위한 방법으로 도입되었다.
  • n-gram이란 본문을 무조건 몇 글자씩 잘라서 인덱싱하는 방법이다.
    • 형태소 분석보다는 알고리즘이 단순하다.
    • 국가별 언어에 대한 이해와 준비 작업이 필요 없다.
    • 만들어진 인덱스의 크기는 상당히 큰 편이다.
  • n-gram에서 n은 인덱싱할 키워드의 최소 글자 수를 의미한다.
  • 일반적으로는 2-gram(또는 Bi-gram) 방식이 많이 사용된다.
    • 각 단어는 다음과 같이 띄어쓰기(공백)와 마침표(.)를 기준으로 구분하고, 2글자씩 중첩해서 토큰으로 분리한다.
    • 10글자 단어라면 (10-1)개의 토큰으로 구분된다.
    • 이렇게 구분된 각 토큰을 인덱스에 저장한다.
    • 이때, 중복된 토큰은 하나의 인덱스로 엔트로 병합되어 저장된다.
  • MySQL 서버는 생성된 토큰들에 대해 불용어를 걸러내는 작업을 수행한다.
    • 불용어와 동일하거나 포함하는 경우 걸러서 버린다.

      -- 기본적으로 MySQL 서버에 내장된 불용어 확인
      SELECT * FROM information_schema.INNODB_FT_DEFAULT_STOPWORD;
  • MySQL 서버는 이렇게 구분된 토큰을 단순한 B-Tree 인덱스에 저장한다.
    • 성능 향상을 위한 Merge-Tree 같은 기능을 가지고 있다.
  • 전문 검색을 더 빠르게 하기 위해 2단계 인덱싱(프런트엔드와 백엔드 인덱스)과 같은 방법도 있다.

불용어 변경 및 삭제

  • 불용어 처리는 사용자에게 도움이 되기보다 사용자를 더 혼란스럽게 하는 기능일 수 있다.
  • 따라서 다음과 같은 방법을 권장한다.
    • 불용어 처리 자체를 완전히 무시하거나
    • MySQL 서버에 내장된 불용어 대신 사용자가 직접 불용어를 등록하는 방법

전문 검색 인덱스의 불용어 처리 무시

불용어 처리를 무시하는 방법은 두 가지가 있다.

  1. 스토리지 엔진에 관계없이 MySQL 서버의 모든 전문 검색 인덱스에 대해 불용어를 완전히 제거

    • MySQL 서버의 설정 파일(my.cnf)의 ft_stopword_file 시스템 변수에 빈 문자열을 설정한다.
    • ft_stopword_file 를 변경하면 MySQL 서버를 재시작해야 변경사항이 반영된다.
  2. InnoDB 스토리지 엔진을 사용하는 테이블의 전문 검색 인덱스에 대해서만 불용어 처리를 무시

    • innodb_ft_enable_stopword 시스템 변수를 OFF로 설정한다.
    SET GLOBAL innodb_ft_enable_stopword=OFF;
    • MySQL 서버의 다른 스토리지 엔진(MyISAM 스토리지 엔진)을 사용하는 테이블은 여전히 내장 불용어 처리를 사용한다.
    • innodb_ft_enable_stopword 는 MySQL 서버가 실행 중인 상태에서도 변경할 수 있다.

사용자 정의 불용어 사용

사용자 정의 불용어를 사용하는 방법은 두 가지가 있다.

  1. 불용어 목록을 파일로 정의한다.

    • MySQL 서버 설정 파일에서 파일의 경로를 ft_stopword_file 설정에 등록한다.
    ft_stopword_file='/data/my_custom_stopword.txt'
  2. 불용어의 목록을 테이블로 저장하는 방식

    • InnoDB 스토리지 엔진을 사용하는 테이블의 전문 검색 인덱스에서만 사용할 수 있다.
    • 불용어 테이블을 생성하고 innodb_ft_server_stopword_table 시스템 변수에 불용어 테이블을 설정한다.
    • 이때, 불용어 목록을 변경한 이후 전문 검색 인덱스가 생성되어야만 변경된 불용어가 적용된다.
    -- 불용어 테이블 생성
    CREATE TABLE my_stopword(value VARCHAR(30)) ENGIN = INNODB;
    INSERT INTO my_stopword(value) VALUES('MySQL');
    
    -- 시스템 변수에 적용
    SET GLOBAL innodb_ft_server_stopword_table='mydb/my_stopword';
    ALTER TABLE tb_bi_gram
    	ADD fuLLTEXT indEX ft_title_body(title, body) With PARSER ngram;
    
    -- cf) 여러 전문 전문 검색 인덱스가 서로 다른 불용어를 사용해야 하는 경우
    SET GLOBAL innodb_ft_user_stopword_table='mydb/my_stopword';

전문 검색 인덱스의 가용성

전문 검색 인덱스를 사용하려면 반드시 다음 두 가지 조건을 갖춰야 한다.

  • 쿼리 문장이 전문 검색을 위한 문법(MATCH … AGAINST …)을 사용
  • 테이블이 전문 검색 대상 칼럼에 대해서 전문 인덱스 보유
-- 전문 검색 대상 칼럼: doc_body
CREATE TABLE tb_test (
	doc_id INT,
	doc_body TEXT,
	PRIMARY KEY (doc_id).
	FULLTEXT KEY fx_docbody (doc_body) WITh PARSER ngram
) ENGIN=InnoDB;

-- 쿼리 문장 예시
SELECT * FROM tb_test
WHERE MATCH(doc_body) AGAINST('애플' IN BOOLEAN MODE);

Reference

참고 서적

📔 Real MySQL 8.0

profile
책을 읽거나 강의를 들으며 공부한 내용을 정리합니다. 가끔 개발하는데 있었던 이슈도 올립니다.

0개의 댓글