2013년 9월 23일 월요일

Fulltext search (Mroonga)


전문 검색 엔진

MySQL 서버에 내장된 전문 검색 엔진은 DELIMITER 방식의 단어 파싱만 제공되고 있어서 사용자들이 띄어쓰기나 문장 기호를 명확히 입력하지 않을 때에는 검색이 불가한 경우가 많다.
그래서 실제로 MySQL의 Builtin fulltext search engine은 많이 사용되지 않으며, 더불어 MySQL builtin fulltext search는 InnoDB 스토리지 엔진을 사용할 수 없는 것도 한가지 문제이다.

이런 단점들을 보완하기 위해서, Lucene나 Sphinx를 도입해서 응용 프로그램 레벨에서 이를 구현하기도 하는데, 이는 많은 개발 코드를 필요로 한다.
그래서 MySQL 서버에 플러그인 형태로 내장되어서 스토리지 엔진 형태로 제공되는 Groonga 라는 fulltext search engine을 검토했으며, 현재 부하가 높지 않은 몇몇 서비스에 적용을 하고 있거나 도입을 검토중이다.

Groonga는 Lucene과 같이 fulltext search engine은 Standalone으로 작동하는 전문 검색 엔진이며,
MySQL Mroonga라는 MySQL 플러그인 스토리지 엔진 fulltext search engine도 제공된다.
Mroonga는 Groonga 라이브러리를 이용해서 MySQL 서버에 플러그인 형태로 내장되는 스토리지 엔진 플러그인이다.

Mroonga는 DELIMITER 형식뿐만 아니라, n-Gram 방식의 형태소 분석및 검색 기능도 제공하며, 일본에서 개발되어서 CJK Language에 더욱 적합하다는 특성도 가지고 있다.
n-Gram 방식의 인덱스를 가지므로 인덱스의 크기가 MySQL Builtin fulltext search engine보다는 크지만, 실제 검색의 Quality나 Performance는 훨씬 낮다는 것이 검증된 상태이다.
참고로 Groonga와 Mroonga는 일본에서 mobage를 운영하는 DeNA라는 회사에서 개발되었다.


전문 검색 엔진 설치
1) 우선 필요한 모듈을 다운로드한다.
groonga-devel-3.0.6-1.x86_64.rpm
Mroonga/groonga-libs-3.0.6-1.x86_64.rpm
groonga-tokenizer-mecab-3.0.6-1.x86_64.rpm
groonga-normalizer-mysql-1.0.5-1.x86_64.rpm
mecab-0.996-1.x86_64.rpm
mecab-ipadic-2.7.0.20070801-6.1.x86_64.rpm
mroonga-3.06.tar.gz
mariadb-5.5.30.tar.gz 
2) 다운로드된  RPM 파일을 필요한 것만 선별적으로 설치한다.
rpm -Uvh groonga-libs-3.0.6-1.x86_64.rpm
rpm -Uvh groonga-devel-3.0.6-1.x86_64.rpm
rpm -Uvh mecab-0.996-1.x86_64.rpm
rpm -Uvh mecab-ipadic-2.7.0.20070801-6.1.x86_64.rpm
rpm -Uvh groonga-tokenizer-mecab-3.0.6-1.x86_64.rpm
rpm -Uvh groonga-normalizer-mysql-1.0.5-1.x86_64.rpm
3) Mroonga와 MariaDB의 소스 코드를 압축 해제한다.
tar zxvf mariadb-5.5.30.tar.gz
tar xzvf mroonga-3.06.tar.gz 
4) Mroonga 소스 디렉토리로 이동한 후, Configure 후 Build를 실행한다.
cd mroonga-3.06
./configure \
--with-mysql-source=/root/install/mariadb-5.5.30 \
--with-mysql-config=/usr/local/mysql/bin/mysql_config

make
5) 빌드가 완료되면, Mroonga plugin shared object를 설치하고, MariaDB의 플러그인 디렉토리에 복사되었는지 확인한다.
make install
ls /usr/local/mysql/lib/plugin/
service mysql restart
*** MySQL 재시작해야 위에서 설치한 rpm을 정상적으로 로드합니다. ***
6) MariaDB에 로그인해서, mroonga 플러그인과 관련 UDF들을 설치한다.
DELETE IGNORE FROM mysql.plugin WHERE name = 'mroonga';
INSTALL PLUGIN mroonga SONAME 'ha_mroonga.so';
CREATE FUNCTION last_insert_grn_id RETURNS INTEGER soname 'ha_mroonga.so';
CREATE FUNCTION mroonga_snippet RETURNS STRING soname 'ha_mroonga.so';

7) 설치가 완료되면, show plugins; show engines; 명령을 이용해서 설치된 Mroonga 스토리지 엔진에 이상이 없는지 확인한다.

전문 검색 엔진용 테이블 생성

0) Mroonga가 Wapper mode와 Storage mode중에서 어떤 모드로 작동할지 선택

  

1) 전문 검색용 테이블 생성
   테이블 생성시, 전문 검색 파서는 일반적으로 "TokenBigramIgnoreBlankSplitSymbolAlphaDigit" 선택 (자세한 내용은 메뉴얼 참조) 
CREATE TABLE tb_test (
    id INT PRIMARY KEY AUTO_INCREMENT,
    content VARCHAR(255),
    FULLTEXT INDEX fx_content (content) COMMENT 'parser "TokenBigramIgnoreBlankSplitSymbolAlphaDigit"'
) ENGINE=mroonga COMMENT='engine "innodb"' DEFAULT CHARSET utf8mb4;

2) 테스트 데이터 적재
INSERT INTO tb_test (content) VALUES ("오늘은 MySQL 공부를 했습니다. 내일도 MySQL 공부를 할 것입니다.");
INSERT INTO tb_test (content) VALUES ("오늘은 MySQL 공부를 했지만, 내일은 Sqlite 공부를 할 것입니다.");

3) 전문 검색 쿼리 테스트
SELECT *, MATCH (content) AGAINST ('MySQL' IN BOOLEAN MODE) as score
FROM tb_test
WHERE MATCH (content) AGAINST ('MySQL' IN BOOLEAN MODE)
ORDER BY MATCH (content) AGAINST ('MySQL' IN BOOLEAN MODE) DESC;
+----+-------------------------------------------------------------------------------------+-------+
| id | content | score |
+----+-------------------------------------------------------------------------------------+-------+
| 1 | 오늘은 MySQL 공부를 했습니다. 내일도 MySQL 공부를 할 것입니다. | 2 |
| 2 | 오늘은 MySQL 공부를 했지만, 내일은 Sqlite 공부를 할 것입니다. | 1 |
+----+-------------------------------------------------------------------------------------+-------+
SELECT *, MATCH (content) AGAINST ('ySQL' IN BOOLEAN MODE) as score
FROM tb_test
WHERE MATCH (content) AGAINST ('ySQL' IN BOOLEAN MODE)
ORDER BY MATCH (content) AGAINST ('ySQL' IN BOOLEAN MODE) DESC;
+----+-------------------------------------------------------------------------------------+-------+
| id | content | score |
+----+-------------------------------------------------------------------------------------+-------+
| 1 | 오늘은 MySQL 공부를 했습니다. 내일도 MySQL 공부를 할 것입니다. | 2 |
| 2 | 오늘은 MySQL 공부를 했지만, 내일은 Sqlite 공부를 할 것입니다. | 1 |
+----+-------------------------------------------------------------------------------------+-------+

참고 URL



적용된 서버

  • KakaoPage의 컨텐츠 검색 (USER :: t_ftsearch)
  • 날씨 테이블의 지역명 검색
  • 사내용 아지트의 게시물 검색 (예정)

주의 사항

1) 테이블은 반드시 옵션을 명시적으로 주고 생성
CREATE TABLE `keywords` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `keyword` varchar(1024) DEFAULT NULL,
 `city_id` int(11) NOT NULL,
 `created_at` datetime NOT NULL,
 `updated_at` datetime NOT NULL,
 PRIMARY KEY (`id`),
 FULLTEXT KEY `fx_keyword` (`keyword`) COMMENT 'parser "TokenBigramIgnoreBlankSplitSymbolAlphaDigit"' 
) ENGINE=mroonga DEFAULT CHARSET=utf8mb4 COMMENT='engine "innodb"'; 
2) 테이블 스키마 변경 시에도 기존에 있는 옵션을 명시적으로 주고 진행
ALTER TABLE keywords modify keyword varchar(2048), ENGINE=mroonga DEFAULT CHARSET=utf8mb4 COMMENT='engine "innodb"'; 
현재, mroonga만 명시한 테이블에 위와 같이 ALTER 구문 실행 시 DB가 종료되는 버그가 있음 (최신 버전에서도 동일 이슈 확인)

댓글 없음:

댓글 쓰기