読者です 読者をやめる 読者になる 読者になる

isuconに参加してきた

isuconに参加してきた

当日の行動

ややうろ覚えですが、自分がやっていたことをざっくり書いてみます。

  • まず、実行用の環境へアクセスしようとしたら、reverse proxyのサーバーへしかsshできなかったので、とりあえずisuconユーザー以下にあるファイルをざっと眺める
  • ソースいじっりするための、reposやdeployスクリプトを作ったり
  • Devel::KYTProfをuseするようにして、ベンチマークを走らてみる

→ 即興で作ったdeployツールのバグに気づかず、Devel::KYTProfのログが出なくてあれー、ということで時間を使う。
→ そもそもClass::Data::Inheritableがうまくuseできていないという問題があったので、とりあえずsudo -H cpanmで入れるようにした。(たぶんarch用のpathもuse libするようにすればよかったはず)

  • KYTProfをonでdeployを動かす
  • MySQLのクエリを解析できるように、log_slow_queryを有効にして、long_query_timeを0にして全てのクエリがslow-logへ吐かれるようにする

だいたいここまでで12時くらい

とりあえずアプリ側をおまかせして、ミドルウェア系のチューニングをやろうかなと思い、

  • メモリ等にも余裕はありそうだったので、starmanのプロセス数を増やしてみた(→ ベンチ速度にはほとんど意味なし )
  • MySQLの設定はほぼデフォルト状態だったので、innodb-buffer-poolやkey-bufferをざっくり設定. ( → データは元々の状態で、ほぼメモリに乘りきっていたので、DBの負荷は高かったがあまり効果はなかったっぽい?)

あたりをやった。

ここまでやってみてアプリ側があんまり進んでいる感じがしなかったので、手を入れはじめた。

KYTProfのログにざーっと目を通すと、
かなり目につくやつは、

SELECT a.id, a.title FROM comment c INNER JOIN article a ON c.article = a.id  GROUP BY a.id ORDER BY MAX(c.created_at) DESC LIMIT 10

だった。いちおうMySQLのslowlogをmysqldumpslowにかけてみて、やっぱりそこが一番遅い、というのを確認してから、作業しはじめた。

複数クエリに分割できないかなー、というのを考えていたのですが、他のチームがぐんぐんスコアあげているのに焦っていて、とりあえず少しでも早めにスコアあげたいということで、とりあえず

SELECT a.id, a.title FROM comment c INNER JOIN article a ON c.article = a.id  GROUP BY a.id ORDER BY MAX(c.id) DESC LIMIT 10

して、少しはましになった。他チームの解法を聞くまで、article側にcommentの最終投稿カラムを持たせればよいだけ、というのに気づかなかったのは間抜けだった。orz ( この時点でスコアは1200程度)

なお懇親会で話を聞いてみると、最終投稿時刻カラムを持たせるかどうか、がスコア10000いくかいかないかの壁だったようだ。

このクエリを改善しても、やはり遅いので、ここはとりあえずmemcachedにのせよう、ということで、memcachedを構築して、使うようにしたりした。

投入してみると、どうやらmemcachedにデータがうまく入っていないらしい、ということで、
調査していた。結局並行で作業を色々していたうちに直ってしまったので、ちょっと気持ち悪いが、おそらくCentOSだとmemcachedの設定は/etc/sysconfig/memcached.confを変更しないといけないが、CentOSに不慣れで/etc/init.d/memcachedの中をいじってしまっていて設定の変更が効いていなくて、デフォルトの状態だとlocalhostからの接続しか受けつけない、とかそういう感じだったのではないかな、と思っているが定かではない。

その間に、memcachedのシリアライザや圧縮あたりの処理が入って無事動きだしたかと思いきや、「サイドバー更新後のコンテンツの更新がありません」チェックにひっかかるようになってしまった。

なので、更新時にキャッシュをクリアするようにしたが、まだダメで、更新時にキャッシュを作るように変更してみた。

だがまだうまくベンチマークのチェックが突破できない、ということで考えていて、Cache::Memcached::Fastの設定でnowaitを1にしていたので、post時に更新した瞬間にアクセスすると、リクエストは終わっていたが、更新されていない、という状態になったりしているのかなー、ということでオフにしてみたら、無事通るようになった。 ( この時点でスコアは5000程度 )

その後もひたすらベンチマークツールを動かしつつ、ログをみつつ、Devel::NYTProfの結果から遅いところを潰していく、という感じ。

DBIのコネクション貼ったり、とか、memcachedへの接続が遅いとか、ネットワーク周りの接続がXenだからか非常に遅かったのが気にかかったが、あまり調査している時間もなかったので、とりあえずはなるべく再接続させないようにインスタンスキャッシュしたりして接続回数を少し減らしたりした。

DBは、thread_cache_sizeやmax_connectionsを増やしてみたが、あんまり効果はなかったように思われる。(あんまり冷静に分析できてはいないけど)

あとは、コンテンツの新規追加後の編集はないので、サイドバー以外の情報はほとんどキャッシュすることができるとわかったので、通常のDBへ対するSELECT文をひたすらmemcachedへ向けていく、というオーソドックスな対応をしていて作業終了。

コンテンツ更新後にサイドバーが更新されていないとチェックが失敗するのに苦しめられつつ、最後には、結局失敗してしまった。

うちのチームがやった変更点は、https://github.com/walf443/isucon に反映されています。

反省

  • あまり人数をうまく活用できなかった
    • 事前打ちあわせとリーダーシップ重要
    • 現状の状況報告と各々がやることを被らないようにはっきりさせる
  • 最初のベンチマークツールを動かした時点で開始から1時間以上たってしまっていた
    • まずは「推測するな、計測せよ」
    • ベンチマークからプロファイルとかしてみた結果から判断しないと、限られた貴重な時間の中で、無駄なことに時間を使ってしまう

とても準備作業大変だったと思いますが、非常に楽しいイベントを用意していただいたライブドア様、ありがとうございました。

こういう参加者がみんな体験を共有できるようなイベントは懇親会とかでも今まで会ったことない人たちでもとても会話しやすくて非常によいですね。

参考: http://d.hatena.ne.jp/karupanerura/20110828/1314469620