Hatena::Groupbugrammer

蟲!虫!蟲!

Esehara Profile Site (by Heroku) / Github / bookable.jp (My Service)
過去の記事一覧はこちら

なにかあったら「えせはら あっと Gmail」まで送って頂ければ幸いです。
株式会社マリーチでは、Pythonやdjango、また自然言語処理を使ったお仕事を探しています

 | 

2013-06-29

[] サービスのデータベース周りなどをチェックしたらレスポンス速度が格段に上がった話 00:48

はじめに

 で、例によってFXでの失敗ケース | Just another WordPress siteの話なのですが、当然のことながらサービスを運用するにあたって、サービスを動かしているサーバーリソースについて、とりあえずは何かあったときのために監視している人が多いと思います。自分も、例に漏れず、万が一のために、Muninを入れています。Munin自体の解説については、下の記事に詳しいかと思うので、そちらを読んで頂ければ。

 で、このMuninはプラグインが充実していて、その一つにhttpingの応答速度を記録しておくという機能があります。httpingというのは、pinghttpヴァージョンです。このあたりについても、下の記事を読んだほうが早いかと思います。

 で、このプラグインを入れてレスポンス速度を見ていたのですが、だいたい1,600msecくらいのレスポンス速度だったわけですね。

 このあたりのチューニングとかに関しては、正直意識したことがなかったので、「まあこんなものなのかなあ、結構データベース参照多いしなあ」という感じで放ったらかしにしていました。

 たぶん、このサービスは自分が一番のファンであり、一番アクセスしているサービスだと思うのですが、やはりレスポンスが若干遅いのは、使っている身からすれば気になる部分であることは間違いない。機能追加も一段落ついたので、ここは一つ、チューニングしてみるかと思ってチューニングした結果が下の通りです。

f:id:nisemono_san:20130630004546p:image

 1,800msecだったレスポンスをなんとか一秒台におさえることができました。やったね!これでちょっとだけ閲覧が楽になるよ!

 というわけで、その作業メモみたいなのを、ここに記録しておきます。

 ここで書かれることは、たぶん多くの普通のエンジニアであるならば意識していることだと思われるので、「なんでそんなこともやってなかったの」と思って頂ければ幸いです。逆に、始めてサービスを作る人で「どうしたらもう少しレスポンスが早くなるのかなー」と悩んでいる人がいたら、参考にして頂ければ幸いです。

まずはサービスのデータベース構成とURLの関係を整理する

 とはいえ、いざチューニングしよう!という話をするとしても、自分のサービス構成がどういう風になっているかを整理しなければ、何処がボトルネックになっているのか、どういうアプローチを取ればいいのかわからないので、その辺を整理してみる必要があるかと。この辺に関しては隠していても仕方ないので、今回の話に関係ある範囲で図にしてみます。

f:id:nisemono_san:20130630004544p:image

 当然、厳密に書こうと思えば、厳密に書けるのですが、取り合えずこのように書くと伝わりやすいかと思います。

 さて、この図を元に、どういうアプローチが取れるかを考えてみます。以下、データベースMySQLを前提としています。

Indexを適切に割り当てる

 まず最初に考えられることは、データベースのカラムに対してインデックスを作るということです。インデックスについては、このような記事などを参照してもらうとして、インデックスが使われるタイミングというのは、どうやら値を比較したときに使われるらしいので、BookのUserあたりに貼り付けると良さげなので設定(要検証)。

 また、他にもインデックスが使えそうな部分として、「最近クリックされた商品をログとして保っておく部分」である、BookLog。ここは、ログが無ければ作成し、ログがあればupdated_atという、更新日時を記録するカラムを更新する作りになっていて、その日付の新しい順番によって、商品を表示するようになっています。

 とすると、このupdated_atの部分にインデックスを貼ることによって、アクセスが早くなる可能性はありそうです。

 ちなみに、インデックスが使われない可能性があるところに関しては、ちゃんとドキュメントに記述してあります。実は、この記事を書いている間に、インデックスが適切に使われているかどうかについて、ちょっと検証が足りない部分があるので、この部分は課題です。

Memcachedを使う

 で、インデックスが終わったあとに、何が効率化できるか。

 このサービスでは、改善前は、アクセスがあったときに、律儀にデータベースに商品をランダムに取りに行っていました。

 しかし、ここで考えるのは、AとBのユーザーが同時にアクセスしたとして別々の商品をランダムに表示させるのは効率が悪いのではないか。各ユーザーにとって、商品がランダムに表示されればいいのではないか。

 なので、そのつど律儀にデータベースアクセスするのではなく、メモリにしばらくの間キャッシュしておいて、それを使った方がいいのではないかと思ったので、そのように実装しました。Memcachedについては、このあたりの記事を参考にするといいのかなと。

ORMで生成されるSQLとの戦い

 djangoもそうですが、フルスタックフレームワークだと、直にSQLを書くのではなく、何らかの形でデータベースアクセスし、クエリを発行するためのオブジェクトが用意されている筈です。

 ただ淡々と書いていると、あまり気がつかなかったりするのですが、明らかに無駄なSQLが発行されている場合もあったりするので、その辺をリファクタリングしていきます。例えば、djangoであるならば、debug-toolbarというものを使って、下のように実際に発行されているクエリをみることができます。

f:id:nisemono_san:20130630004545p:image

 ちなみに、この場合だと、"ORDER BY RAND()"で少し時間がかかっている印象です。知人に相談した処、「ORDER BY RAND()は使うなボケ、それはプログラムで処理しろ」と言われたので、その辺を改善する必要がありそうです。

まとめ

 というわけで、普通のエンジニアであるならば「当たり前のことを」という感じですが、実際に自分でサービスを作ってみると、本当にさっぱりになるので、やはりデータベース周りを勉強する必要性を痛感するのでした。あとは、状況と判断で適切なチューニングが出来るようにスキルをあげられたら嬉しいところです。

 |