スマートスタイル TECH BLOG|データベース&クラウドの最新技術情報を配信

InnoDB の全文検索機能におけるキャッシュ利用について

MySQL 5.7 において InnoDB の全文検索機能が日本語に対応するようになって約 3 年が経ちました。今では InnoDB で日本語の全文検索ができることは広く知られるようになってきたのではないでしょうか。そんな中、今回は MySQL 8.0 においてもまだ修正されていない、全文検索の制限について確認していきたいと思います。

MySQL 5.7 の日本語全文検索とは

まず、MySQL 5.7 において何が変わったのかについて簡単におさらいしたいと思います。
MySQL 5.6 まではデフォルトで FULLTEXT パーサーが使用されていたため、日本語のように単語の初めと終わりが明確でない言語で FULLTEXT インデックスを使用しようとした場合、単語を区切ったり N-gram で分割した状態で DB に格納するなど、使用するためには DB のデータを変形して格納する必要がありました。

※なお、WITH PARSER 句で全文パーサーを指定することはできたため、N-gram や MeCab などを用いた全文検索パーサーを独自に準備すれば、プラグインとして追加することは可能でした

MySQL 5.7 では、日本語対応の全文検索パーサーとして N-gram がインストールされており、MeCab もプラグインとして使用できるように準備されているため、パラメータを追加すればすぐにインストールすることが可能になりました。

全文検索機能の制約について

MySQL 5.7 において非常に使いやすくなった全文検索機能ですが、innodb_ft_result_cache_limit というパラメータの存在に注意する必要があります。
引用:14.14 InnoDB Startup Options and System Variables

The InnoDB full-text search query result cache limit (defined in bytes) per full-text search query or per thread. Intermediate and final InnoDB full-text search query results are handled in memory. Use innodb_ft_result_cache_limit to place a size limit on the full-text search query result cache to avoid excessive memory consumption in case of very large InnoDB full-text search query results (millions or hundreds of millions of rows, for example). Memory is allocated as required when a full-text search query is processed. If the result cache size limit is reached, an error is returned indicating that the query exceeds the maximum allowed memory.

The maximum value of innodb_ft_result_cache_limit for all platform types and bit sizes is 2**32-1.

このパラメータは「実行中を含んだ全文検索クエリーの結果(Intermediate and final InnoDB full-text search query results)」に対してのキャッシュ上限を制御しています。
どういうことなのか、実際に検証して確認していきます。

検証環境

  • MySQL 5.7.23 (innodb_ft_min_token_size=1 以外はデフォルト値)
  • 以下のテーブルを用意します

※ comment 列にはランダム文字列ではなく、青空文庫などから文章をインポートしています

検証内容

ngram_token_size = 2 のとき

まずは、実際に FULLTEXT インデックスがどのように格納されているかを確認します。MySQL 5.7 のデフォルトでは ngram_token_size の値は 2 であるので、2 文字区切り(bigram)でインデックスが作成されていることがわかります。

続いて、全文検索関数を使用して全文検索クエリーを実行します。

このとき、innodb_ft_result_cache_limit はデフォルト値である 2000000000 になっているため、このクエリーには 2GB までのキャッシュが使用できることになります。
上記クエリーが実行できる閾値を探したところ、約 4MB のところで実行できなくなりました。

このとき、実際のクエリー結果も 3.5MB 程であったため、結果にそこまで乖離はないと考えられます。

ngram_token_size = 1 のとき

続いて、ngram_token_size を 1 にして同様の検証をおこないます。ngram_token_size は動的変数ではないので、変更後は mysqld を再起動して、FULLTEXT インデックスを再作成する必要があります。

また、innodb_optimize_fulltext_only を一時的に有効にして OPTIMIZE TABLE を実行することで、INNODB_FT_INDEX_TABLE にインデックス情報を直ちにディスクに書き込むことができます

このとき、先ほどと同じクエリーを実行してエラーになる閾値を確認すると、11MB 程でした。

クエリーの結果自体は ngram_token_size = 2 と同じく 3.5MB 程であるため、実際の結果より多くのキャッシュを使用していることがわかります。
この他、ngram_token_size = 3 および MeCab パーサーを使用したときの innodb_ft_result_cache_limit の閾値を調査したところ、以下のようになりました。

Full-Text Parser ngram_token_size innodb_ft_result_cache_limit
N-gram 1 11395798
N-gram 2 3852022
N-gram 3 1000000(最小値)
MeCab 3693430

区切り文字が短くなる(検索の精度があがる)程、クエリーの実行に必要なキャッシュが大きくなっているのがわかります。

問題点

innodb_ft_result_cache_limit における問題点は主に 2 点あると考えられます。

パラメータの上限値

Bug #86036においても指摘されていますが、このパラメータは上限値が 2**32-1(約4GB) しかありません。
そのため、全文検索を使用したくなるような行数の多いテーブルにおいては、クエリーの実行に必要なキャッシュが innodb_ft_result_cache_limit の上限値を超えてしまいやすくなるため、肝心なときに使用できない、といった事態が起こりやすくなります。

メモリーの使用量

innodb_ft_result_cache_limit の閾値は、クエリーを実行している時セッションが実際に使用しているメモリー量と等しくなります。
そのため、Bug #84195においても指摘されていますが、たとえば innodb_ft_result_cache_limit を 3GB にしないと実行できないような全文検索クエリーは、クエリの実行中 3GB のメモリーを使用してしまいます。そのため、全文検索クエリーが同時に実行されるような環境では、事前にクエリーの負荷を確認しておかないと、メモリーの急騰に耐え切れなくなる恐れがあります。

まとめ

上記の問題点は、MySQL 8.0.12 現在、まだ修正されていません。また、innodb_ft_result_cache_limit = 4000000000 を設定しても実行できない(「FTS query exceeds result cache limit」となる)クエリは、InnoDB の全文検索機能を使用できないため、サードパーティ製の全文検索エンジン(Mroonga)などで代替する必要があります。
それでも、InnoDB を使用したまま全文検索がカジュアルにおこなえるのは他にはない利点ですので、今後、全文検索機能がどのように発展していくのか、注目していきたいと思います。

Return Top