ククログ

株式会社クリアコード > ククログ > index_column_diffコマンドのインデックス破損の誤検知を解消

index_column_diffコマンドのインデックス破損の誤検知を解消

最近、Groongaindex_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値の扱い
インデックス更新時 Mroongaは、NULL値を無視します。 => Mroongaは、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.0Mroonga 14.10をすでにリリースしていますので、ぜひご利用ください。

最後に、この記事が皆さんのGroongaやMroongaの理解の一助となれば幸いです。 もし、Groongaの利用でお困りのことがありましたら、 ぜひ、お問い合わせよりご連絡ください。