본문 바로가기
Database/MYSQL

InnoDB 전문 검색 : N-gram Parser

by 반화넬 2023. 9. 2.
반응형

Mysql n-gram parser fulltext search
Mysql N-gram


기본 InnoDB 전문 검색(Full Text) 파서는 공백이나 단어 분리자가 토큰인 라틴 기반 언어들에서는 이상적이지만 개별 단어에 대한 고정된 구분자가 없는 중국어, 일본어, 한국어(CJK)같은 언어들에서는 각 단어는 여러개의 문자들의 조합으로 이루어집니다. 그래서 이경우엔 단어 토큰들을 처리할 수 있는 다른 방법이 필요합니다.

우리는 CJK에서 사용할 수 있는 n-gram 파서를 제공하기 위한 새로운 플러그블 전문 파서(pluggable full-text parser)를 MySQL 5.7.6 에서 제공할 수 있게되어 정말 기쁩니다.

N-gram이 정확히 뭘까요?

 

Mysql n-gram parser fulltext search
Mysql N-gram


전문 검색에서 n-gram은 주어진 문자열에서 n개 문자의 인접한 순서입니다. 예를들어 n-gram 을 이용해 우리는 “abcd” 문자열을 다음과 같이 토큰나이즈합니다.

N-gram 예제Shell
N=1 : 'a', 'b', 'c', 'd';
N=2 : 'ab', 'bc', 'cd';
N=3 : 'abc', 'bcd';
N=4 : 'abcd';

InnoDB에서 n-gram 파서를 어떻게 사용할 수 있을까요?

새 n-gram 파서는 기본적으로 로드되고 활성화되어 있기때문에 그것을 사용하기 위해서는 여러분의 대상 DDL문들에 WITH PARSER ngram 문을 간단히 기술하기만 하면 됩니다. 예를들어 MySQL 5.7.6 과 그 이후버전에서는 다음의 문장을 모두 사용할 수 있습니다.

 

반응형



N-gram DDL 예제Shell
mysql> CREATE TABLE articles
(
        FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
        title VARCHAR(100),
        FULLTEXT INDEX ngram_idx(title) WITH PARSER ngram
) Engine=InnoDB CHARACTER SET utf8mb4;
Query OK, 0 rows affected (1.26 sec)
 
mysql> # ALTER TABLE articles ADD FULLTEXT INDEX ngram_idx(title) WITH PARSER ngram;
mysql> # CREATE FULLTEXT INDEX ngram_idx ON articles(title) WITH PARSER ngram;



MySQL 5.7.6에서는 ngram_token_size(토큰은 n 문자로 만들어진 단어와 거의 동등함)라는 새로운 글로벌 서버 변수도 도입되었습니다. 기본값은 2(bigram)이고 1에서 10까지 변경 가능합니다. 다음 질문은  토큰사이즈를 어떻게 선택할까? 일 것입니다. 일반적인 경우엔 2 또는 bigram이 CJK에서 권장되어 지지만, 여러분은 아래 간단한 규칙에 따라 유효한 값을 선택할 수 있습니다.

규칙 : 여러분이 검색하려고 하는 가장 큰 토큰으로 토큰 사이즈를 설정한다.

만약 단일 문자를 검색하려면, ngram_token_size을 1로 설정해야합니다.  ngram_token_size가 작은 쪽이 인덱스를 작게 할 수있어 그 인덱스를 이용한 전체 텍스트 검색이 더 빨라집니다. 그러나 단점은 당신이 검색 할 수있는 토큰 크기를 제한하는 것입니다. 예를 들어 영어의 “Happy Birthday”전통적인 중국어로는 ‘生日高興” 라고 번역됩니다. ( ‘Happy’== ‘高興’,’Birthday’=’生日’)이 예와 같이 각 단어/토큰은 2 문자로 구성되기 때문에이 토큰을 검색하기 위해서는ngram_token_size을 2 이상으로 설정해야합니다.

N-gram 토큰화의 상세


n-gram 파서는 기본적으로 전문(full text) 파서와 다음과 같은 차이점이 있습니다.

토큰 크기 : innodb_ft_min_token_size과 innodb_ft_max_token_size는 무시됩니다. 대신 토큰을 제어하기 위해 ngram_token_size을 지정합니다.


스탑워드 처리 : 스탑워드(stopwords) 처리도 조금 다릅니다. 일반적으로 토큰 화된 단어 자체(완전히 일치)가 스탑워드 테이블에 있다면 그 단어는 전문 검색 인덱스에 추가되지 않습니다. 그러나, n-gram 파서의 경우, 토큰화된 단어에 스탑워드가 포함되어 있는지 확인하고 포함된 경우엔 인덱스를 생성하지 않습니다. 이렇게 동작이 다른 이유는 CJK에서는 매우 빈번하게 사용되는 무의미한 문자, 단어, 문장 부호를 가지고 있기 때문입니다. 스탑워드와 일치하는 문자가 포함되어 있는지를 확인하는 방식을 사용하면 쓸모없는 토큰을 더 많이 제거 할 수 있습니다.
공백 : 공백은 항상 하드 코드된 스탑워드입니다 예를 들면, ‘my sql’는 항상 ‘my’, ‘y’, ‘s’, ‘sq’, ‘ql’로 토큰화되어지고 ‘y’와 ‘s’는 인덱싱되지 않습니다.


우리는 INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE 테이블과 INFORMATION_SCHEMA.INNODB_FT_TABLE_TABLE 테이블을 참조하여 특정 전문 검색 인덱스에 어떤 토큰이 인덱스화되어 있는지 정확하게 확인할 수 있습니다. 이것은 디버깅을 위해 매우 유용한 도구입니다. 예를 들어, 단어가 예상대로 전문 검색 결과에 표시되지 않는 경우, 그 단어는 어떤 이유 (스탑워드, 토큰, 크기, 등)로 인덱스화되어 있지 않은지, 이 테이블을 참조하여 확인할 수 있습니다. 간단한 예를 소개합니다.

간단한 디버깅 예제Shell

mysql> INSERT INTO articles (title) VALUES ('my sql');
Query OK, 1 row affected (0.03 sec)
 
mysql> SET GLOBAL innodb_ft_aux_table="test/articles";
Query OK, 0 rows affected (0.00 sec)
 
mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE;
+------+--------------+-------------+-----------+--------+----------+
| WORD | FIRST_DOC_ID | LAST_DOC_ID | DOC_COUNT | DOC_ID | POSITION |
+------+--------------+-------------+-----------+--------+----------+
| my   |            1 |           1 |         1 |      1 |        0 |
| ql   |            1 |           1 |         1 |      1 |        4 |
| sq   |            1 |           1 |         1 |      1 |        3 |
+------+--------------+-------------+-----------+--------+----------+
3 rows in set (0.00 sec)

 

 

N-gram 검색 처리의 상세



텍스트 검색

NATURAL LANGUAGE MODE에서 검색되는 텍스트는 n-gram의 합집합으로 변환됩니다. 예를 들어, ‘sql’는 ‘sq ql’로 변환됩니다 (기본 토큰 크기인 2 또는 bigram의 경우).


NATURAL LANGUAGE MODE 예제Shell

mysql> INSERT INTO articles (title) VALUES ('my sql');
Query OK, 1 row affected (0.03 sec)
 
mysql> SET GLOBAL innodb_ft_aux_table="test/articles";
Query OK, 0 rows affected (0.00 sec)
 
mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE;
+------+--------------+-------------+-----------+--------+----------+
| WORD | FIRST_DOC_ID | LAST_DOC_ID | DOC_COUNT | DOC_ID | POSITION |
+------+--------------+-------------+-----------+--------+----------+
| my   |            1 |           1 |         1 |      1 |        0 |
| ql   |            1 |           1 |         1 |      1 |        4 |
| sq   |            1 |           1 |         1 |      1 |        3 |
+------+--------------+-------------+-----------+--------+----------+
3 rows in set (0.00 sec)

 


BOOLEAN MODE 에서 검색되는 텍스트는 n-gram 구문 검색으로 변환됩니다. 예를 들어, ‘sql’은 ‘ “sq ql”‘로 변환됩니다.

번역자 주석  : “sq ql”는 ‘sq’와 ‘ql’가 순서대로 모두 일치해야하므로 ‘sq’나 ‘ql’은 검색 결과에 나오지 않는다.

BOOLEAN MODE 예제Shell

mysql> SELECT * FROM articles WHERE MATCH(title) AGAINST('sql' IN BOOLEAN MODE);
+------------+--------+
| FTS_DOC_ID | title  |
+------------+--------+
|          1 | my sql |
|          2 | my sql |
|          3 | mysql  |
+------------+--------+
3 rows in set (0.00 sec)

 

 

와일드 카드를 사용한 검색


접두사 (prefix)가 ngram_token_size 보다 작은 경우, 검색 결과는 그 접두사로 시작하는 n-gram 토큰을 포함한 모든 행을 반환합니다.

 

Prefix 예제Shell

mysql> SELECT * FROM articles WHERE MATCH (title) AGAINST ('s*' IN BOOLEAN MODE);
+------------+--------+
| FTS_DOC_ID | title  |
+------------+--------+
|          1 | my sql |
|          2 | my sql |
|          3 | mysql  |
|          4 | sq     |
|          5 | sl     |
+------------+--------+
5 rows in set (0.00 sec)

 


접두사 길이가 ngram_token_size과 같거나 큰 경우 와일드 카드를 사용한 검색은 구문 검색으로 변환되고 와일드 카드는 무시됩니다. 예를 들어, ‘sq *’는 ‘ “sq”‘로 변환되어 ‘sql *’는 ‘ “sq ql”‘로 변환됩니다.
Prefix 예제 2Shell

mysql> SELECT * FROM articles WHERE MATCH (title) AGAINST ('sq*' IN BOOLEAN MODE);
+------------+--------+
| FTS_DOC_ID | title  |
+------------+--------+
|          1 | my sql |
|          2 | my sql |
|          3 | mysql  |
|          4 | sq     |
+------------+--------+
4 rows in set (0.00 sec)
mysql> SELECT * FROM articles WHERE MATCH (title) AGAINST ('sql*' IN BOOLEAN MODE);
+------------+--------+
| FTS_DOC_ID | title  |
+------------+--------+
|          1 | my sql |
|          2 | my sql |
|          3 | mysql  |
+------------+--------+
3 rows in set (0.00 sec)

 

구문 검색



구문 검색은 n-gram 토큰 문구 검색으로 변환됩니다. 예를 들어, “sql”는 “sq ql”로 변환됩니다.
구문 검색 예제Shell

mysql> SELECT * FROM articles WHERE MATCH (title) AGAINST('"sql"' IN BOOLEAN MODE);
+------------+--------+
| FTS_DOC_ID | title  |
+------------+--------+
|          1 | my sql |
|          2 | my sql |
|          3 | mysql  |
+------------+--------+
3 rows in set (0.00 sec)


mysql> SELECT * FROM articles WHERE MATCH (title) AGAINST ('"my sql"' IN BOOLEAN MODE);
+------------+--------+
| FTS_DOC_ID | title  |
+------------+--------+
|          1 | my sql |
|          2 | my sql |
+------------+--------+
2 rows in set (0.00 sec)
 
mysql> SELECT * FROM articles WHERE MATCH (title) AGAINST ('"mysql"' IN BOOLEAN MODE);
+------------+-------+
| FTS_DOC_ID | title |
+------------+-------+
|          3 | mysql |
+------------+-------+
1 row in set (0.00 sec)

InnoDB의 전문 검색 전반에 관하여 자세히 알고 싶다면, 사용자 설명서 InnoDB Full-Text Index 부분과 Jimmy의 기사 (Dr. Dobb’s article)를 참조하십시오. N-gram 파서 대한 자세한 내용은 사용자 설명서의 N-gram parser 부분을 참조하십시오.

Mysql n-gram parser fulltext search
Mysql N-gram

마무리.

Mysql 엔진의 진화는 진행중이죠. 많은 서비스에서 이용하고 있고 그리고 엔진이 안정적으로 RDMS로 성장해가고 있답니다. 아직 오라클 DB와 비교 할수는 없겠지만 그래도 다양한 분산 서비스를 이용하고 검색엔진등을 이용한다면 안정적인 서비스 운영으로 Mysql 많나  DB  없을것 같습니다.

 

감사합니다.

 

참조 : https://dev.mysql.com/blog-archive/innodb-full-text-n-gram-parser-ko/

반응형