:selectで取得するカラムを絞ったらパフォーマンスが倍に
最近管理しているDBサーバで継続的にスロークエリが出るようになったので、チューニングしてみたら気持ちの良い結果が出た。
結論から言うとカラム数が多いテーブルに対しては:selectで取得するカラムを絞るのがかなり有効かと思う。
現状把握
今回スロークエリの発生していたテーブルの状況を整理したのが以下。
- レコード件数は110万件くらい
- カラム数は30程度
- インデックスは効いている(explainで確認済み)
- 処理の性質的にキャッシュは使えない
スロークエリになっているのはもっぱら以下のクエリ。
select * from pages order by updated_at limit 100;
Railsのコードで見るとこんなかんじ。
Page.all(:order => 'updated_at', :limit => 100)
こんな単純なクエリが実行に2秒から10秒程度もかかってスロークエリとして記録されているのは切ない。
インデックスは効いているので問題解決には他のアプローチが必要になる。
考えるに対象は30以上カラムがあってレコードサイズもそこそこ大きいテーブル。
そこで取得するカラムを絞って余計なカラムを取得しないようにしてみたらどうかと思った。
というかクエリが単純すぎてまずはそれくらいしか浮かばなかったわけだけど。
ベンチマークとチューニング
計測なくしてチューニングなしということでベンチマークで使ったのはmybench。
ベンチマークとチューニングは手元の開発環境で実行した。
こちらレコード件数は3万件程度。本番環境より大幅に少ないが十分だろう。たぶん。
全カラム取得とカラムを絞った結果の比較が以下。
10クライアントから100回ずつ、計1000回のリクエストを送るというのを試行回数3回ずつ行った結果。
serialは経過時間(秒)です。
まずは全部まるごと取得している現状のクエリ。
select * from pages order by updated_at limit 100;
# Page.all(:order => 'updated_at', :limit => 100)
serial : 29.173278
serial : 29.433684
serial : 30.258237
これを取得カラムを絞ったものにしてみると。
select id, updated_at from pages order by updated_at limit 100;
# Page.all(:select => 'id, updated_at', :order => 'updated_at', :limit => 100)
serial : 16.422306
serial : 17.562543
serial : 16.070013
倍近く速くなった。
うつくしい。
いやぁ、チューニングって本当に気持ちがいいものですね。
これを本番環境にアップしたらスロークエリもパッタリなくなり幸せになれました。
以下詳しく見たい人向け
select * from pages order by updated_at limit 100;
# Page.all(:order => 'updated_at', :limit => 100)
test: 1000 0.001631 0.060372 0.029173278 29.173278 342.779443571614
clients : 10
queries : 1000
fastest : 0.001631
slowest : 0.060372
average : 0.029173278
serial : 29.173278
q/sec : 342.779443571614
test: 1000 0.001535 0.06981 0.029433684 29.433684 339.746801657584
clients : 10
queries : 1000
fastest : 0.001535
slowest : 0.06981
average : 0.029433684
serial : 29.433684
q/sec : 339.746801657584
test: 1000 0.00298 0.065291 0.030258237 30.258237 330.488521191767
clients : 10
queries : 1000
fastest : 0.00298
slowest : 0.065291
average : 0.030258237
serial : 30.258237
q/sec : 330.488521191767
select id, updated_at from pages order by updated_at limit 100;
# Page.all(:select => 'id, updated_at', :order => 'updated_at', :limit => 100)
test: 1000 0.000327 0.037233 0.016422306 16.422306 608.927881382797
clients : 10
queries : 1000
fastest : 0.000327
slowest : 0.037233
average : 0.016422306
serial : 16.422306
q/sec : 608.927881382797
test: 1000 0.001182 0.050836 0.017562543 17.562543 569.393623691057
clients : 10
queries : 1000
fastest : 0.001182
slowest : 0.050836
average : 0.017562543
serial : 17.562543
q/sec : 569.393623691057
test: 1000 0.000301 0.04706 0.016070013 16.070013 622.277032383234
clients : 10
queries : 1000
fastest : 0.000301
slowest : 0.04706
average : 0.016070013
serial : 16.070013
q/sec : 622.277032383234