MySQLでJSON型を使う(タグ検索の実装編)

MySQL 8.0

MySQLでJSON型を使う(タグ検索の実装編)

過去に、MySQL 5.7とMySQL 8.0でJSON型を使うBlog 記事を連載を行いました。

上記の MySQL8.0でJSONの配列にインデックスを使う で取り上げた複数値インデックスを活用する実装例を紹介させていただきます。

動作検証は MySQL 8.0.41で行っていますが、MySQL 8.0.17以降のバージョンなら動作するはずです。

InnoDB; JSON: InnoDB now supports multi-valued indexes on JSON arrays. A multi-valued index is an index in which multiple index records can point to the same data record.

目次

一般的なタグ検索の実装例

一般的にタグ検索の機能を持ったアプリケーションを実装する場合は、タグと検索対象の関係がN:Mになるので、それぞれのテーブルのPK同士を関連付けるマッピングテーブルを作成する実装するのではないでしょうか?

この実装は、実はいくつか避けられない問題を持っています。

  • 複数のタグを組み合わせたAND/OR条件の検索の実装が少し面倒 (例えば、上記のクエリを単純にINに書き換えると重複が発生するので、OR条件の検索にはならない)
  • 大量のオブジェクトに大量のタグが付く場合、N:Mマッピングするテーブルの行数が増えて性能問題の原因になる

JSONを利用したタグ検索の実装例

JSONの複数値インデックスは、JSON型カラムの一部に対して複数の値を持ったインデックスを作成することが可能になります。

複数値インデックス

この機能を活用すると、Objectsテーブル側にJSON型のカラム内の配列に対して複数値インデックスを作ることが可能になるためタグのような機能の実装に使えます。

この時に使うのが、MEMBER OF関数です。MEMBER OF関数の値として、サブクエリを使えないので、クエリが2回に分かれてしまいますが以下のようなクエリを書く必要があります。

これでは少し不便ですし、タグのAND/OR条件の検索も行いたいので、JSON_CONTAINS関数とJSON_OVERLAPS関数を用います。

  • JSON_CONTAINS(target, candidate[, path])

特定の candidate JSON ドキュメントが target JSON ドキュメント内に含まれているかどうか、または path 引数が指定されているかどうか (候補がターゲット内の特定のパスで見つかったかどうか) を 1 または 0 を返して示します。

  • JSON_OVERLAPS(json_doc1, json_doc2)

2 つの JSON ドキュメントを比較します。 2 つのドキュメントに共通のキーと値のペアまたは配列要素がある場合、true (1) を返します。 両方の引数がスカラーの場合、この関数は単純な等価性テストを実行します。

JSON_CONTAINS関数は、第一引数のJSONドキュメントに第二引数の配列の全ての値が含まれている場合に、TRUE(=1)を返す仕様なのでAND条件と同じに挙動になります。
JSON_OVERLAPS関数は、第一引数のJSONドキュメントに第二引数の配列の一部の値が含まれていれば、TRUE(=1)を返す仕様なのでOR条件と同じ挙動になります。
また、どちらも第二引数にJSONの配列を渡せるので、この部分にサブクエリを組み込みます。

上記のクエリでちゃんとインデックスが使われているのかを確認するために、Explain とExplain analyzeを取得してみました。
ちゃんと tag_masterテーブルからtag_idのJSON配列を作成するサブクエリは、1回しか実行されない"select_type: SUBQUERY"で、json_indexテーブルから、i_json_tagsを使って該当行を抽出できています。

このように N:Mのマッピングするテーブルを使わずに、シンプルなテーブル構成でタグ検索の実装ができました。

MySQLのJOIN方法は基本的にNested loop joinなので、N:Mのマッピングするテーブルを使う設計で実行時に駆動表が大きくなると途端に性能が落ちます。

複数値インデックスを使ってテーブルから単純にデータを取り出す方法ならば、JOINが不要になります。

複数値インデックスの注意点

複数値インデックスが使える値の型には大きな制限があります。
基本的には数値型などの特定の型だけです。使いやすいJSON型や文字列型などは、CASTエラーが出て使えません。

マニュアルの中に使える型の種類が書かれていないようです。どの型が使えるかどうかの判定のロジックがソースコードの以下の部分に書かれているので、興味のある方は見てみるとよいかもしれません。

https://github.com/mysql/mysql-server/blob/8.0/sql/item_create.cc#L1884

まとめ

JSON型で使える複数値インデックスを使ったタグ検索の実装サンプルを書いてみました。

JSON型は、他のデータ型に比べて内部に複雑な情報を構造化して保存できるため、適切に扱えばデータの柔軟性と関数インデックスを用いた高速な検索の両方を得ることができます。

皆様の日々の開発を行う際の参考になれば幸いです。

スマートスタイルTECHブログについて

スマートスタイルTECHブログでは、日頃MySQLのサポート業務に従事している有資格者で構成された技術サポートチームがMySQLに関する技術情報を発信しています。データベースのお困りごとはお気軽にご相談下さい。

よかったらシェアしてね!
  • URLをコピーしました!
目次