EXPLAINで意図したインデックスが使われなかったときにRailsで USE INDEX を指定する方法

MySQL
記事内に広告が含まれています。

はじめに

MySQLでクエリのパフォーマンスを調査する際にまず行うのが EXPLAIN による実行計画の確認です。
しかし、インデックスを張っているにもかかわらず、意図したインデックスが使われていないというケースがあります。
この記事では、そういったケースに遭遇したときに、Rails(ActiveRecord)から MySQLの USE INDEX を指定して、明示的にインデックスを使わせる方法を紹介します。

想定する状況

たとえば、Railsのマイグレーションで以下のように blogs テーブルに複数のインデックスを追加したとします。

add_index :blogs, :user_id
add_index :blogs, :category_id

これにより、次の2つのインデックスが作成されます。

  • index_blogs_on_user_id
  • index_blogs_on_category_id

この状態で以下のようなクエリを実行したとします。

Blog.where(user_id: 1024).order("category_id DESC")

このとき、Railsが生成するSQLは以下のようになります。

SELECT * FROM blogs WHERE user_id = 1024 ORDER BY category_id DESC;

このクエリでは user_id を条件にしていますが、EXPLAIN を確認すると、なぜか index_blogs_on_user_id ではなく index_blogs_on_category_id が使われていたということがあります。

これは、MySQLが ORDER BY に基づいた並び替えのコストを下げるために、category_id のインデックスを優先して使おうとするためです。
しかし、検索条件の高速化を目的として user_id のインデックスを使いたい場合には、この挙動が望ましくないということがあります。

.from を使って USE INDEX を指定する

MySQLでは、インデックスの使用を明示的に促すために USE INDEX という構文が用意されています。
これをRailsで利用するには、ActiveRecordの .from メソッドにSQL文字列を直接渡します。

Blog.from('blogs USE INDEX(index_blogs_on_user_id)').where(user_id: 1024).order("category_id DESC")

このとき、Railsが生成するSQLは以下のようになります。

SELECT * FROM blogs USE INDEX(index_blogs_on_user_id) WHERE user_id = 1024 ORDER BY category_id DESC;

このように、MySQLに対して意図したインデックスの使用を促すことができます。

注意点

USE INDEX はインデックスの「使用を促す」ヒントであり、実際に使用されるかどうかはMySQLのクエリオプティマイザの判断に委ねられます。

FORCE INDEX を使ってインデックス使用を強制する

どうしても特定のインデックスを使わせたい場合は、USE INDEX ではなく FORCE INDEX を使うこともできます。

Blog.from('blogs FORCE INDEX(index_blogs_on_user_id)').where(user_id: 1024).order("category_id DESC")

これは、MySQLに対してそのインデックスを「強制的に使え」と指示するものです。
ただし、強制することで逆にパフォーマンスが悪化する可能性もあるため、慎重に使用する必要があります。

おわりに

EXPLAIN を使ってクエリのチューニングをしていると、本来使われるべきインデックスが使われていないことが少なからずあります。

そうした場面では、Railsでも .from(... USE INDEX(...)).from(... FORCE INDEX(...)) を使うことで対処できる場合があります。

パフォーマンス改善の選択肢の一つとして、ActiveRecordにおけるインデックス指定の手段を知っておくと有効な場面が出てくるでしょう。

最後までお読みいただきありがとうございました。

皆さんからのコメントやSNSでのシェア、嬉しい投稿をいただくと本当に励みになります。

もしこの記事が気に入ったら感想をコメントやSNSでシェアしていただけると嬉しいです。

皆さんの声を聞かせてくださいね!

MySQL
tetsuをフォローする
簿記はじめるってよ

コメント

タイトルとURLをコピーしました