MAGAZINE

ルーターマガジン

データベース

Mroongaのエスカレーションをやめた話

2022.08.03
Pocket

Mroongaのエスカレーションをやめた話

弊社のサービスであるアドクロールの全文検索エンジンにMroongaを使用しています。Mroongaは国産のオープンソース全文検索エンジンで、メンテナーも日本人ということもあり日本語対応もしていただけるのが嬉しいポイントです。

ある日、Mroongaエンジンのテーブルに全文検索クエリが複数並列で流れました。いつもなら遅くとも数秒の全文検索クエリがいつまで経っても結果が返ってきません。やがてはDBのメモリが溢れて落ちてしまいました。

今回はDBが落ちてしまった原因である、Mroongaのデフォルト設定のエスカレーションについて紹介します。

エスカレーションとは

簡単に言うと検索条件を緩めることです。Mroongaでは転置インデックスを使用します。転置インデックスとは、ある単語Aは文書Bと紐付いているという情報を持っているリストです。予めそのリストを持っておくことで高速に検索できるのです。

具体的には下記の様な転置インデックスと検索対象のテーブルがあったときに、検索クエリ「googl」を与えたとします。「googl」は辞書には登録されていないので、ヒット結果は0件です。しかし、それではユーザー体験としてはあまり良く有りません。タイプミスをしているかもしれないのですから、「googl」でも「google」を含むレコードがヒットしてほしいと考えます。それならと「googl」の前方一致で辞書を検索します。ありました、「google」です。転置インデックスに登録された「google」にはID=1が紐付いているので、検索テーブルのID=1のレコードを返します。 これが検索のエスカレーションです。

転置インデックス
辞書転置リスト
apple2
google1
iphones2
pixels1
sells1, 2
検索テーブル
IDtext
1google sells pixels
2apple sells iphones

DBが落ちたシチュエーション

MroongaのデフォルトトークナイザーはTokenBigramです。TokenBigramは日本語文はbi-gramで、英語文は単語単位で区切り、辞書登録されます。今回のDBが落ちてしまったのは、英語文の検索パターンに該当します。URLが約1,000万入ったテーブルで特定のSQLが複数並列時に発生しました。

URL全文検索

URLは英語の文章でも特殊な文字列です。アクセストークンやらクエリパラメータやらがくっついてきます。今回のようにDBに負荷をかけるURLを紹介します。

https://groonga.com/mroonga-01q

このURLの場合は単語に分解され、最後のトークンはqとなります。単語1文字でエスカレーションが起きたらどうなるでしょうか。qの前方一致検索となるのです。つまり、qで始まる全単語がヒットします。そりゃ時間かかるわけだ。

DBが落ちてしまったのはエスカレーションによる1文字単語の前方一致検索でした。 この件についてはMroonga(Groonga)のメーリングリストで回答を頂いています。詳細はメーリングリストをご覧ください。 https://osdn.net/projects/groonga/lists/archive/dev/2022-May/004995.html https://osdn.net/projects/groonga/lists/archive/dev/2022-June/005003.html

Mroongaのエスカレーション設定

Mroongaのエスカレーション設定はデフォルトで0になっています。この変数はエスカレーションを行う閾値を表しています。(参考:mroonga_match_escalation_threshold

つまり、検索結果が0件のときにエスカレーションして検索されます。

エスカレーション設定の確認

SHOW variables LIKE 'mroonga_match_escalation_threshold';
+------------------------------------+-------+
| Variable_name                      | Value |
+------------------------------------+-------+
| mroonga_match_escalation_threshold | 0     |
+------------------------------------+-------+

永続的にエスカレーションをしないように設定

エスカレーションの閾値が必ず結果セットの件数よりも下回ればいいので、先の変数に-1を設定します。公式リファレンスにはグローバル変数をセットする方法しか記載されていませんが、永続的にエスカレーションをしたくない場合はmy.cnfに以下を追加してください。

[mysqld]
loose_mroonga_match_escalation_threshold=-1

デフォルトが-1でもいいような気がしました。

あとがき

MroongaはGroongaをMariaDB(MySQL)で使えるようにしたものです。そのため、Groongaのリファレンスもかなり参考になりました。全文検索といえばElasticsearchやApache Solr、Apache Lucene、tantivy等々ありますが、日本語サービスならGroongaも技術選定リストに加えるのはいかがでしょうか。

Pocket

CONTACT

お問い合わせ・ご依頼はこちらから