最近はRoRばかり触っているので生SQLを見る機会はかなり減ったが
それでもSQLのパフォーマンスというのはアプリケーションエンジニアの必須科目であると思う。
レスポンスタイムが0.1秒かかるものを0.05秒にしたいとかいうレベルの話であればそれはそもそもSQLのチューニングの領域を超えている可能性があるが、世の中のレスポンスタイム遅延というのはほとんどが、10秒を1秒以内にしたいとか行ったっきりブラックホールに吸い込まれて二度と戻って来ないような処理を数秒以内に収めたいとかそういう類の話であるように思う。
私は常に書いた処理のパフォーマンスを心配するタイプの人間なので、世の中の「まあ動けばええんやろ」的精神で書かれているコードを見ると「大丈夫?それ返ってくる?」と不安な気持ちになる。
オプティマイザの実行計画を読み解くこと自体そもそも別にそんな複雑なことではないのだが
それ以前の問題として、DBアクセスをする時に気をつけるポイントというのは限られていると思っている。
つまり、
・浅はかなテーブル設計を行っていないか
・駆動表を間違っていないか
・インデックスは適切に張られているか
・インデックス走査を想像しながらSQLを書いているか
ここらへんを守れば基本的にデータ件数が100万や1000万になろうと、大体の場合は致命的なパフォーマンス遅延は発生しない。はずである。
これらは決して難しい話ではなく、ほとんどは”意識”の問題である。
頭の隅に置くか置かないかで、実装されるSQLは大きく変わる。
一つずつ見ていこう。
・浅はかなテーブル設計を行っていないか
これはよく言われる話だが、重要なのは「浅はかな」という部分。
どれだけ気をつけてテーブル設計をしても、要件が発展したり後々改定を進めていくうちに段々と当初思い描いていた理想のカッチョイイ設計からは離れていくものである。これはもう仕方ない。
重要なのはそれ以前の話で、そもそも明らかにこれはまずい/まずい匂いがする設計を勢いで突っ走らないことが大事である。
例えば「数千万件が登録されるテーブルと結合させるのにどう繋げてもインデックスが効かない構成になっている」とか、「数万以上のレコードがあるテーブルの抽出条件がsubstr(column, 1, 3)
のような関数を当てる条件しか無い」とか。これはもう設計の時点で気づける話である。
・駆動表を間違っていないか
これは特に規模の大きいシステムにおいては非常に大事である。
大きいシステムになるとテーブルは山のようにあり、テーブルをあっちこっち繋げていたら何十ものjoin祭りになっていることもままある。
こういった時に一番気をつけたいのは、「駆動表で最も効率的にレコード数を絞れているか」という話。
駆動表とはつまりjoinの根っこになる大元のテーブルのことだが、これがいくら直接絞っても1000万件で、10個先のjoinで100レコードになるとしても、そのSQLはかなりの高確率で致命的な遅さになる。オプティマイザの知能に頼るのはあまり得策とは言えない。
原則としてSQLとは、まず最初に最大限対象を絞って、そのあとに付加情報をjoinして広げていくものであると私は思う。そのSQLの駆動表は本当にそのテーブルで合っているだろうか?
・インデックスは適切に張られているか
これは改めていう必要は無いと思うが、SQLパフォーマンスのほとんどの問題はインデックスの構成で解決する。
insert/update等によるインデックス再構築のコストを考えてあまりインデックスを張らないという考えもあるかもしれないが、ほとんどの場合はそれらは問題にならず、大体問題になるのはインデックスを張っていない場合である。もちろんこれはシステムの種類によるが、よほどの大規模トラフィックで無い場合は、これは張ろうかな、やめようかな、と悩むくらいなら張ってしまった方が良い場合が多い。
・インデックス走査を想像しながらSQLを書いているか
これができれば、ほとんどの事故は未然に防げる。もし事故が起きたとしても、対処方法はすでに描けているはずである。
・駆動表でどのくらい絞られて
・どのテーブルにどのインデックスを使って結合されて
・どこでインデックスの恩恵を失うのか
を描きながらSQLを書くことができれば、パフォーマンス事故を未然に防ぐ確率がかなり上がるはずだ。
いつもいつもこれを考慮しながらコードを書くのは面倒かもしれないが、少し重いSQLを書く時に意識していきたい。
自戒を込めて。