Ruby
Rails
MySQL
ActiveRecord

Railsエンジニアなら知っておきたい!RDBでindex作成を検討するときってどんなとき?

More than 1 year has passed since last update.


はじめに

前回に引き続き、またまたDBネタです(^o^)

前回:Railsエンジニアなら最低限これだけは知っておきたいSQLのJOINの動き

今回は、インデックスについてです。インデックスにはいくつか種類がありますが、

RDBで一般的に使われるB-treeインデックスについて書いていきます。

いきなりですが、インデックスは深い!かなり深い!バイカル湖くらい深いです。

ある程度の指針的なものはありますが、インデックスをどう設計するかの見極めは状況によって変わってくるようです。クエリの実行頻度、テーブルサイズ、カーディナリティ(カラム内のデータの種類の多さ)などなど。。

なので今回は、こんなときはインデックス作成を検討した方がいいというパターンだけザッとまとめる感じで行きたいと思います。

なお、今回の記事作成にあたっては、以下の本を参考にさせて頂きました。

そもそも的なところから分かりやすく書かれていたので、オススメです。

理論から学ぶ データベース実践入門

https://www.amazon.co.jp/dp/4774171972


インデックスとは


なんで必要か?メリットって?

インデックスは索引という意味です。本の後ろにあるアレです。

調べたい用語が順番通りに整理されていることで、すぐに目的のページにいけます。

RDBのインデックスは、検索を高速化するために(も)使われます。

テーブルのカラムに対してインデックスを作成することでインデックスが存在する場合のテーブルスキャンを高速に行えるようになります。効果としては、whereでの検索、Joinによる結合、orderによるソート等が効率的に行えるようになるため、様々な面での性能向上が期待できます。


デメリットもあるよ

しかし一方で、無闇矢鱈とインデックス作成するものでもありません。

インデックスを作成することで、テーブル更新(追加、更新、削除)を行った際にインデックスを更新することになるため、余計なお仕事をRDBMSはすることになります(更新処理のオーバーヘッドが増える)。

また、インデックスを作成することで余計にディスクスペースも必要となります。


インデックスを作成する前に

インデックスを作成する前に正規化は行いましょう!

カラムの量が多くなり1テーブルに対して作成するインデックスの量が増え、

テーブル更新の際のオーバーヘッドが増え、DBとしてのパフォーマンスが低下します。


インデックスの作成を検討するとき


WHERE句を使うとき


不等号(<, >)や等号(=)による検索

不等号(<, >)や等号(=)による検索を行う場合、インデックスを検討しましょう。

また、同じテーブル内の複数のカラムに対して検索条件を設定している場合、

マルチカラムインデックス(複合インデックス)を使うことでより効率的な検索が行える可能性があります。

それぞれのカラムでインデックスを作成するのではなく、複数のカラムを組み合わせてインデックスを作成します。

例えば、カラムA > 10 AND カラムB = 'sale'のような検索条件の際に、

(カラムB, カラムA)のようにマルチカラムインデックスを設定することで、

カラムBで絞った中からカラムAが10以上の値を検索することができます。

ここで1つ注意点があります。

複合インデックスは正しく設定すると大きく性能向上を期待できますが、インデックスの順番がとても重要です。例えば、A,Bのような順番でインデックスを作成した場合、Aでの検索にしかインデックスが使えません。Aの範囲検索をした段階で、カラムBがsaleの値も削除されてしまう可能性があります。

またマルチカラムインデックスに効果がある場合は、ANDで結合された場合のみです。ORだと段階的な絞り込みができませんので、その場合はそれぞれのカラムに対して、インデックスを作成する必要があります。

なお、(カラムB,カラムA)でマルチカラムインデックスを設定し、WHEREでカラムBのみ検索した場合、カラムBの検索で、マルチカラムインデックスが使うことができます。マルチカラムインデックスの先頭要素は、シングルインデックスのように使うことができます。


LIKEによる完全一致および前方一致検索を行うとき

LIKEでも範囲検索にインデックスが使える場合がありますが、完全一致もしくは前方一致のみでしか有効ではありません。


JOIN(結合)するとき

JOINを行う際にもインデックスは有効です。

結合されるテーブル(内部表と呼ばれる)へのアクセスにはインデックスが使われますが、それ以外に、WHERE句を使って結合条件を設定している場合は、そのカラムに対してインデックスを設定することにより、JOINを行った際の性能向上が期待できます。


ソートするとき

Bツリーインデックスはキーの順番通りにソートされ作成されるため、ORDER_BYでソートを行う際にもインデックスが有効に働きます。

インデックスされたデータはすでにソートされて状態であるため、前または後ろからデータを取り出すことで順番通りデータを取り出すことができます。

条件を絞ってからソートしたデータを取得する際は、前述したように、マルチカラムインデックスを検討し、インデックスの順番に気をつけましょう!


インデックスの作成をしないとき

前章ではインデックスの作成を検討する場合をいくつか紹介しましたが、

上記に条件に当てはまるからと言って作ればよいというわけではありません。

インデックスをしない方がパフォーマンスが良い場合もあります。更新パフォーマンスが悪くなること、ディスクスペースも必要なることを忘れないようにしましょう!

1)インデックスを使うクエリの実行頻度が低い(1日数回とか)

2)テーブルのサイズが非常に小さい(検索しても大して変わらない)

3)インデックスを使ってもあまり絞り込めない

4)インデックスを使わなくても十分絞り込めている(マルチインデックスのときとか)


インデックスが効かない検索

インデックス作成を検討する場合でも、条件によってインデックスが効かないときもあります。十分に注意しましょう!

1)LIKEでの部分一致、後方一致で検索している

2)インデックスの列に演算を行っている

3)WHERE句で否定(<>)を使っている


インデックスがきちんと使われているかのチェック

オプティマイザーによりインデックスが使われているかをチェックするためには、

MySQLならEXPLAINを使いましょう。EXPLAINの詳細については参考リンクを参照ください!


最後に

インデックスの設計はとても難しいと感じた次第です。

冒頭で紹介した本に、「インデックス設計は組み合わせ最適化問題」と書いてあり、まさに!と思いました。

また、適切なインデックスは機能追加やデータ量等によって変化するため、

その時点であまりに追求しすぎてもあまり意味がないこともあるので妥協することも重要とありました。ということで、インデックスとは長い付き合いになりそうですw


参考リンク

Ruby on Rails on MySQL チューニング入門

https://www.slideshare.net/eccyan/ruby-on-rails-on-mysql

MySQLのEXPLAINを徹底解説!!

http://nippondanji.blogspot.jp/2009/03/mysqlexplain.html

オトコのソートテクニック2008

http://nippondanji.blogspot.jp/2008/12/2008.html

RailsエンジニアのためのSQLチューニング速習会

https://www.slideshare.net/naominami908/railssql

データベース/複合インデックスの落とし穴

https://www.gatc.jp/gat/it/it02dbindex.html

SQL実行計画の疑問解決には「とりあえずEXPLAIN」しよう

https://thinkit.co.jp/article/9658

複合インデックス

http://use-the-index-luke.com/ja/sql/where-clause/the-equals-operator/concatenated-keys