最近、Groongaのindex_column_diff
コマンドの誤検知を解消した児玉です。
index_column_diff
はインデックスの破損を検出するコマンドですが、
このコマンドが誤検知を起こすケースがあったので解消しました。
この記事では、index_column_diff
の詳細と誤検知の原因およびその解消方法を紹介します。
ここから先は、Groongaのインデックスの仕組みを理解していることを前提として進めていきます。 インデックスの仕組みを知らないよという方は、次の記事を読んだ後に戻ってくると理解しやすいと思います。
index_column_diffについて
本題に入る前に、index_column_diff
コマンドについて紹介します。
簡潔に言うと、index_column_diff
は、インデックスの破損をチェックできる便利コマンドです。
ここでは、簡単な使い方、仕組みとユースケースを紹介します。
使い方
使い方はとてもシンプルで、次のように引数を指定して実行するだけです。
index_column_diff table_name index_column_name
table_name
: チェックするインデックスカラムを含むテーブルの名前を指定します。index_column_name
: チェック対象のインデックスカラムの名前を指定します。
ただし、利用時には注意点もありますので、詳細はindex_column_diffコマンドのドキュメントをご覧ください。
仕組み
index_column_diffコマンド
は、次の2つのポスティングリストを比較してインデックスの破損を検出する仕組みになっています。
- 検出対象のインデックスカラムに保持されているポスティングリスト
index_column_diff
が、インデックス対象のソースカラムから新たに作成したポスティングリスト
どちらも同一のソースカラムから作成されているポスティングリストなので、 両者が一致しない場合は、ポスティングリストの整合性が保たれておらず、インデックスが破損していると判断できます。
ユースケース: インデックス破損が疑われる場合
Groongaでは基本的にインデックスの破損は起こりません。 しかし、インデックスが破損していると、検索結果の不整合やシステムのクラッシュを引き起こす可能性があります。 そのため、期待した検索結果が得られない場合やインデックス破損が疑われる場合には、インデックスの状態をチェックすることが重要です。
そこで役立つのがindex_column_diff
コマンドです。
このコマンドを実行することで、インデックスの破損を検出できます。
また、問題のあるレコードやトークンを特定できるため、原因の解明や再発防止にも役立ちます。
原因の特定が難しい場合やGroonga側に問題がある場合は、開発元に報告しましょう。
報告する際には、index_column_diff
で得られた情報も提供すると、
開発元が問題を調査し、原因の修正や適切な対策を提供する際の手助けになります。
誤検知の原因とその解消方法について
さて、index_column_diff
コマンドへの理解が深まったところで、
本題でもあるindex_column_diff
の誤検知について説明します。
今回のインデックス破損の誤検知は、次の2つの原因から発生していました。
- ケース1: Mroongaでの
NULL
値の扱い - ケース2: 転置索引のトークン数の上限設定
それぞれの原因と解消方法について紹介します。
ケース1: MroongaでのNULL
値の扱い
原因
このケースは、Mroongaで発生しました。 (MroongaはMySQL・MariaDB・Percona ServerからGroongaを使うためのプラグインです。)
Mroongaは、NULL
をサポートしていません。なのでNULL
を登録した際の挙動は不定になっています。
ただし、SQLの仕様上、カラムがNULL
を許容する場合はNULL
を登録できてしまいます。
今回の誤検知は、NULL
が登録されると発生する可能性がありました。具体例を交えて紹介します。
たとえば、blog_id
というINT型のカラムがNULL
を許容しているとします。
NULL
に対して、次のような挙動となっていました。
操作 | NULL値の扱い |
---|---|
インデックス更新時 | Mroongaは、NULL 値を無視します。(インデックスに追加しない。) |
データ読み込み時 | index_column_diff は、NULL 値を0 として扱います。 |
index_column_diff
コマンドでは、インデックス破損をチェックする際に既存データを読み込んで確認します。
そのため、NULL
値も0
として読み込みます。
この挙動の差異により、本来インデックスに存在しないはずの0
という値がインデックスに含まれるべきと判断され
差分として検出されていました。これが、誤検知の原因です。
解決策
インデックス更新時に、NULL
値が登録された場合でも、各カラムに適したデフォルト値でインデックスを更新するように変更しました。
具体的には、今回のblog_id
というINT型のカラムにNULL
が登録された場合、0
としてインデックスに追加します。
つまり次のように挙動を変更しました。
操作 | NULL値の扱い |
---|---|
インデックス更新時 | NULL 値を無視します。NULL 値を0 としてインデックスに追加します。 |
データ読み込み時 | index_column_diff は、NULL 値を0 として扱います。 |
これにより、index_column_diff
コマンドがインデックス破損をチェックする際のNULL
の扱いと、
実際のインデックスの値が同じになるため、インデックス破損の誤検知を解消できました。
注意: MroongaでのNULL
の扱いは引き続き不定です。この挙動に依存せず、NULL
値をカラムに登録しないでください。
実際にどんな変更を入れたのか気になる人は次のPRをご覧ください。
ケース2: 転置索引のトークン数の上限設定
原因
Groongaの転置索引には、1つのレコード内で同一トークンが出現できる数に約13万という上限があります。 たとえば、長大なテキストデータや繰り返しの多いデータをトークナイズした場合に、この上限を超える可能性があります。 この上限を超えると、インデックス更新時には超過分のトークンは無視され、インデックスに追加されません。
しかし、index_column_diff
コマンドで破損チェックをするためのデータ生成時には、
この上限を設定していませんでした。そのため、インデックスには存在しない超過分のトークンも比較対象となり、
差分として検出されていました。これが、インデックス破損の誤検知の原因でした。
解決策
index_column_diff
コマンド実行時の比較用データ生成時にも、
転置索引のトークン数の上限(約13万)を設定するように変更しました。
これにより、インデックス更新時と同じ条件で比較用のデータを生成できるようになりました。
この修正により、index_column_diff
コマンドの挙動がインデックス更新時の挙動と一致するようになり、
誤検知を解消できました。
実際にどんな変更を入れたのか気になる人は次のPRをご覧ください。
まとめ
今回、index_column_diff
コマンドで発生していたインデックス破損の誤検知についての原因と解決策を紹介しました。
- Mroongaでの
NULL
値の扱い: インデックス更新時にNULL
値を適切なデフォルト値で処理するように変更 - 転置索引のトークン数の上限設定:
index_column_diff
でも上限を設定するように修正
これらの対応により、index_column_diff
を用いたインデックスの破損チェックがより正確になりました。
この変更をいれたGroonga 14.1.0とMroonga 14.10をすでにリリースしていますので、ぜひご利用ください。
最後に、この記事が皆さんのGroongaやMroongaの理解の一助となれば幸いです。 もし、Groongaの利用でお困りのことがありましたら、 ぜひ、お問い合わせよりご連絡ください。