bokko bokkoにしてやんよ

A infrastructure and software engineer's blog

isucon予選に 「くらげとみかんと江戸幕府」チームで参加してきました

同僚の@walf443@edvakfとisucon予選一日目に参加してきました。

僕がくらげで@walf443がみかん、そして@edvakfが江戸幕府です。

16:30頃の時点でベンチマークが2〜3時間以上通ってなかった上に RawScore(エラーによる減点を除いたスコア)も散々だったので完全に通夜状態だったのですが、 17:00頃から一気に巻き返して最終的には予選一日目5位(19412.5点)で通過(暫定)することができました。

今回は主に僕と@edvakfでアプリケーションの修正をやって、 @walf443がミドルウェアまわりをやるという役割でスタートし、 最終的には僕もアプリケーションの修正をやる傍らミドルウェアの置き換え等を行う形になりました。

言語はRubyにするかPHPにするかで意見が割れていたのですが最終的に僕の強い希望でPHPに決まりました。(予選通過(暫定)できて本当に良かったです。。。)

ここからは実際にやったことを主に僕視点で解説していきます。(@walf443の解説はこちら)

初期状態で既にエラーが出る

公式ブログにもあるようにPHPの場合は初期状態でも過負荷で必ずベンチマークでエラーが出てしまうのですが、 最後までこのエラーは解消できませんでした。

でも今回はベンチマーク時にエラーが出てもエラーの数が少なければ ちょっと減点される程度でスコア自体は登録できる仕組みになっていたのでこのエラーはひとまず無視してアプリケーションの修正にとりかかることにしました。

しかし、この時点で既に12:00を過ぎており、一時は「やっぱりRubyにしようか」という話にもなりましたが、 このタイミングで(エラー出てたけど)ちょうどベンチマークが成功したのでギリギリPHPで行くことになりました。

スロークエリを探す

ベンチマークを実行している間にtopを眺めていると明らかにMySQLが詰まっていたので、@walf443に pt-query-digestを動かしてもらってその出力結果から一番重いクエリを書きかえたりインデックスを貼ったりして対応していました。(実際に貼ったインデックスや書き換えたクエリ等については@walf443の解説を参照)

PHP5.4 -> PHP5.5 with ZendOpcache and APCu

もっとも上記の作業は複数人でやるとコンフリクトが起きそうだったので、修正やインデックスの作成はほかの人に任せて 僕の方ではPHPのバージョンを5.5にあげてZendOpcacheやAPCuの設定をしていました。

PHP5.5をインストールした直後にPDOまわりが動かなくなって焦りましたが、冷静な@walf443のおかげでなんとか解決。

APCuでひたすらキャッシュ

今回は「SELECT count(*) …」みたいな集計クエリが結構見られたのでこの結果をAPCuでキャッシュしていきました。

Apacheの前段にNginxを配置

初期設定では静的ファイル(css, js, img)もpreforkのApacheで返していたので、前段にNginxを配置してそちらで返すようにしました。

なお、gzip圧縮をOnにするとスコアが下がる罠があったそうですが、この時点ではそこまで気が回らなかったのでこの罠にはかからずに済みました。

実はNginxはOpenRestyでインストールしたんだけど、今回は使わなかった。というか使うところまで行けなかった。

ベンチマークが常に失敗するようになる

序盤で出遅れつつもRawScoreは少しずつ上がってきていたのですが、中盤からベンチマークが常に失敗する自体に。。。

エラーは元々最初から出ていたのですが、このあたりからエラーの数が臨界点を突破したのか常に失敗する状態になりました。

アプリケーションの修正やミドルウェアの設定は続けていましたが、この状態が何時間も続いたので 15:00頃は「今日の晩御飯どうしようかな」とか本気で考えていた気がします。

workloadを上げて対処

16:30を過ぎた頃、どうせ失敗するであろうベンチマークをtopを眺めていたところ、 ベンチマークプログラムがCPUを使い切れていないことに気づいたので試しにworkload(ベンチマークの並列度)を上げてみたら 相変わらずエラーは出るもののベンチマークは常に成功するようになりました。

どうもこのエラーはベンチマークのスコア(=アプリケーションのパフォーマンス)が良くなると出にくくなるようです。

ようやくベンチマークが安定するようになったもののこの時点でスコアはまだ4〜5000点なので予選通過には程遠い状態でした。

魔のmarkdown

ベンチマークをかけている間にtopを眺めているとMySQLのCPU使用率が30〜40%まで下がってきたかわりにちょくちょくmarkdownのプロセスが顔を出すようになりました。

これは(PHPの場合は)shell_execで外部プロセスを呼び出し、markdownのテキストをHTMLに変換するという処理になっていたので、 PHPのmarkdownパーサーに差し替えることで対処しました。

これでスコアが一気に倍の10000点を越え、希望の光が見えてきました。残り一時間。。。

ラストスパート

実はこの後何をしたのかよく覚えていませんw。引き続き@walf443がMySQLのパラメータを変更したり、 @edvakfがアプリケーションの修正をやってる間に僕がベンチマークをかけ続けていたらいつの間にかスコアが19412.5点まで上がっていました。

前回の反省を踏まえて

前回のisucon#2に参加したときは手当たり次第にボトルネックになりそうなところから手をつけて最適化していたんですが、 ほとんど外れで事前にボトルネックを分析するの大事だなぁと思っていたので今回はボトルネックの分析により時間をかけました。 まぁ、実際にやったのはほとんど@walf443ですが。

本戦に向けて(暫定)

最初に書いたようにラスト1時間前まで通夜状態だったので予選一日目5位に滑り込んだ時は大はしゃぎだったのですが、 やっぱり優勝するならもっとドラスティックな変更(ホットスポットをOpenRestyで書き直すとか)を入れないと駄目だなぁと思いました。

まだ予選通過は暫定ですが、本戦出れたら頑張りたいと思います。