mita2 database life

主にMySQLに関するメモです

InnoDB FULLTEXT Search の ngram_token_size

ngram_token_size パラメータは InnoDB FTS の ngram パーサーの設定です。ngram_token_size に指定した文字数ごとに文章をトークナイズします。

疑問

  • ngram_token_size をインデックス作成後から変更すると、どうなるのか?
  • あり得るパターン
    1. CREATE INDEX した時の ngram_token_size をインデックスが保持しており、変更後も既存のインデックスは影響を受けない
    2. ngram_token_size 変更前後で、1つのインデックスに異なるトークンサイズのトークンが存在してしまう

試してみます

mysql> SET GLOBAL ngram_token_size=3;
ERROR 1238 (HY000): Variable 'ngram_token_size' is a read only variable

おっと、、、ngram_token_size はオンラインで変更できなかった、、、 この時点で、パターン2臭がする、、、

まず、token_size=2 で インデックスを作ります。

mysql> SHOW GLOBAL VARIABLES LIKE 'ngram_token%';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| ngram_token_size | 2     |
+------------------+-------+
1 row in set (0.00 sec)

mysql> INSERT INTO fts.fts_check (col1) VALUES('にぐらむ');
Query OK, 1 row 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 |
+--------+--------------+-------------+-----------+--------+----------+
| ぐら   |            6 |           6 |         1 |      6 |        3 |
| にぐ   |            6 |           6 |         1 |      6 |        0 |
| らむ   |            6 |           6 |         1 |      6 |        6 |
+--------+--------------+-------------+-----------+--------+----------+
3 rows in set (0.00 sec)

ngram_toke_size=2、2文字区切りで、トークナイズされていることが確認できました。 次に、ngram_toke_size=3 にして、データを追加します。

mysql> SHOW GLOBAL VARIABLES LIKE 'ngram_token%';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| ngram_token_size | 3     |
+------------------+-------+
1 row in set (0.00 sec)

mysql> INSERT INTO fts.fts_check (col1) VALUES('トリグラム');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM information_schema.INNODB_FT_INDEX_TABLE;
+--------+--------------+-------------+-----------+--------+----------+
| WORD   | FIRST_DOC_ID | LAST_DOC_ID | DOC_COUNT | DOC_ID | POSITION |
+--------+--------------+-------------+-----------+--------+----------+
| ぐら   |            6 |           6 |         1 |      6 |        3 |
| にぐ   |            6 |           6 |         1 |      6 |        0 |
| らむ   |            6 |           6 |         1 |      6 |        6 |
+--------+--------------+-------------+-----------+--------+----------+
3 rows in set (0.00 sec)

mysql> SELECT * FROM information_schema.INNODB_FT_INDEX_CACHE;
+-----------+--------------+-------------+-----------+--------+----------+
| WORD      | FIRST_DOC_ID | LAST_DOC_ID | DOC_COUNT | DOC_ID | POSITION |
+-----------+--------------+-------------+-----------+--------+----------+
| グラム    |            8 |           8 |         1 |      8 |        6 |
| トリグ    |            8 |           8 |         1 |      8 |        0 |
| リグラ    |            8 |           8 |         1 |      8 |        3 |
+-----------+--------------+-------------+-----------+--------+----------+
3 rows in set (0.00 sec)

パターン2 でした!新しく登録したレコードは設定変更の影響を受けて3文字区切りでトークナイズされてる。 1つのインデックスに2文字区切りと、3文字区切りでトークナイズされた結果が混ざってしまっています。

念の為、検索して、確認してみます。

mysql> SHOW GLOBAL VARIABLES LIKE 'ngram_token%';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| ngram_token_size | 3     |
+------------------+-------+
1 row in set (0.00 sec)

mysql> SELECT * FROM fts_check WHERE MATCH(col1) AGAINST (+'にぐ' IN BOOLEAN MODE);
+----+--------------+
| id | col1         |
+----+--------------+
|  1 | にぐらむ     |
+----+--------------+
1 row in set (0.00 sec)

-- ngram_token_size =  3 なので、「にぐら」でインデックスから検索する。
-- インデックスには「にぐ」しかないのでヒットしない。
mysql> SELECT * FROM fts_check WHERE MATCH(col1) AGAINST (+'にぐら' IN BOOLEAN MODE);
Empty set (0.01 sec)

-- インデックスに「トリ」はないのでヒットしない
mysql> SELECT * FROM fts_check WHERE MATCH(col1) AGAINST (+'トリ' IN BOOLEAN MODE);
Empty set (0.00 sec)

mysql> SELECT * FROM fts_check WHERE MATCH(col1) AGAINST (+'トリグ' IN BOOLEAN MODE);
+----+-----------------+
| id | col1            |
+----+-----------------+
|  4 | トリグラム      |
+----+-----------------+
1 row in set (0.00 sec)

まとめ

  • ngram_token_size を変更したら、インデックスを再作成しないと、既存のインデックスに反映されない
mysql> OPTIMIZE TABLE fts_check;
+---------------+----------+----------+-------------------------------------------------------------------+
| Table         | Op       | Msg_type | Msg_text                                                          |
+---------------+----------+----------+-------------------------------------------------------------------+
| fts.fts_check | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| fts.fts_check | optimize | status   | OK                                                                |
+---------------+----------+----------+-------------------------------------------------------------------+
2 rows in set (0.05 sec)
  • そして、スキーマやテーブルごとにngram_token_sizeを変更することはできない