木曜日の社内ISUCONにチームぽわわ3.5として参加してきました。 (今年のISUCON本番に4にアップデート予定) さきに結果だけ書いておくと、 1位はfujiwaraさんとacidlemonさんのチーム、 2位はチームぽわわ3.5、 3位はぴっぴ先輩率いるチーム例の青い紐でした。
オムライスと紐を倒したので僕は満足です。 簡単にやったことを書いておきます。
課題内容
Twitterみたいな短文投稿サイトです。 トップページにアクセスすると全ユーザの発言最新100件がみれて、 ログインすると発言したり自分の投稿履歴を確認したりできます。 僕が新卒で入ってきたときはPerlでしたが、今年の参考実装はGolang製です。 (Rubyもあったらしいけど使った人いたのかな)
やったこと
僕自身は、相方になったたいがさんに「こんなことしてみては〜」と言ってみる係をやってました。 具体的な対応としては以下の通りです。
nginxにレスポンス吐かせる
Nginxのレスポンスタイムをパーセンタイル値で計測するMunin plugin とかを参考にしてもらって、レスポンスタイムを吐くようにしてもらいました。
ログをテキトウスクリプトで集計したとろこ、トップページの全ユーザの発言最新100件みれるページが重いみたい。 高速化の第一ターゲットをトップページにしぼりました。
MySQLにSlowQuery吐かせる
トップページが重いっぽいというのはわかったものの、 どのクエリが重いかまでは分からない(もちろんコード読んでたので検討はついてたけど)ので、 処理に0.1秒以上かかっているクエリを吐くようにしました。
インデックスの追加
既存のコードに触れずにお手軽ってことで、まずはDBにインデックスを張るところから。 workload10で、99583から101033にスコアアップ! まあ、他のボトルネックを潰していない段階だとこんなもんでしょうね・・・。
ループクエリ・無駄クエリの削除
明らかに無駄クエリっぽいところがあったのでそこを修正しました。
- 投稿100件取得したあとに、100回ユーザ名の取得処理をしている
- JOINを使って書き換えました
- 実行計画が狂って逆に遅くなるという事態に陥ったので、IGNORE INDEXとかして頑張った
- ユーザの投稿を全取得してるのに、最新1件の情報しか使ってないところ
- LIMITをつけて制限
- 全投稿をCOUNTしているところ
- せっかくGolang使ってるんだから楽しようと、グローバル変数に突っ込んでcount++してみた
「グローバル変数に突っ込んでみた」対策みたいに、下手にアプリサーバで情報を保持すると DBとアプリサーバに差ができてしまうので、実運用では避けるべきテクニックですね。 あとになって考えると、ベンチ回す前にアプリサーバの再起動忘れてたのにベンチ通ってたので、 投稿数数えなくてもよかったのでは・・・。
nginxによる静的ファイルの配信
cssとかjsをGolangでかえしていたので、nginxで返すようにしました。 これで724338から802905(workload:100)にScoreアップ!
画像の縮小
Twitterらしく投稿には100x100程度のサイズのアイコンが表示されるんですが、 元画像が1000x1000程度だったので縮小しました。 ただ、ベンチが画像にアクセスしにこないので、まったくの効果なし。 最終計測では結局元画像に戻しました。 実運用では確かに効果があると思うんですが、まずはログを見て判断しろという教訓ですね。
まとめ
あとはworkloadの調整とかやって最終スコアは935519でした。 2位にはなったものの、インデックス追加とかループクエリの削除とか最低限のことが何とか出来たって感じです。 もっと精進します。
tech kayac へのポストまだかな〜