ISUCON に参加してきました

livedoor 主催の、いい感じにスピードアップコンテスト ISUCON (Iikanjini Speed Up CONtest) に参加してきました。
ちょっとふざけた名前(失礼)ながら、中身は本格的で、用意されたプログラムやベンチマークは、過去に実際に起きた問題に似せた形で出題されたそうです。

「チームでの参加を推奨します」とは言われてたのですが、ボッチ力を発揮して一人での参加。

チーム名は「くまさんちーむ」でした。

結果

1分ベンチでは2万強。最終的な結果(3分ベンチ)では 56,151 request というスコアを出せました。
残念ながら、優勝された fujiwara組には遠く及ばないスコアでしたが、記録としては上位に入れましたし、一人でここまで出せたので満足です。

実際にやったのは以下の通り。

DB にテーブルを追加し、アプリケーションのクエリを書き換え

やはり、一番のネックとなっていたのは DB の負荷でした。
その中でも、おや? と思うクエリを発見したので、それをなくすためテーブルを追加しました。

CREATE TABLE `last_comment` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `article` int(11) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

というテーブルを作成し、コメントのpost 時に、

INSERT INTO last_comment SET article = [article] 

を実行。

サイドバー取得のクエリを

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;

から

SELECT a.id, a.title 
 FROM last_comment c INNER JOIN article a ON c.article = a.id 
 ORDER BY c.created_at DESC LIMIT 10;

に書き換えました。


最初は、article の最終コメント日時を10件のみ 記録するテーブルを作ろうと思ったのですが、perl で日時を比較する方法が分からず、ほとんどクエリのみの調整で対応したが為にこうなりました。

ただ、これだと、最初のリクエストでは last_comment に何も入っていないので、

TRUNCATE TABLE last_comment;
INSERT INTO last_comment (article,created_at) 
 SELECT article, max(created_at)
 FROM comment GROUP BY article ORDER BY MAX(created_at) DESC LIMIT 10;

MySQL の起動時に実行するように、起動スクリプトを書き換えました。

実際にこんな対応の仕方をするのはあり得ない(コメントが増えたら元の問題と同じ現象が発生する)のですが、まぁ、こういう場ですし ;-)

(あるいは定期的に last_comment を初期化することで回避できますよね)

Memory ストレージエンジン

DB のストレージエンジンを Memory にしました。
まずは、

max_heap_table_size = 3G

これを my.cnf に設定。DB のメモリは4G あったのですが、データ以外でも使う部分があると思ったので、余裕を持って3Gにしました

その上で、

ALTER TABLE article ENGINE=Memory;
ALTER TABLE comment ENGINE=Memory;
ALTER TABLE last_comment ENGINE=Memory;

を行いました。

ただし、このままだと MySQL を停止したときにデータが消えてしまいます。

そこで、起動スクリプトを弄って、停止時に InnoDB に変換、起動時に Memory に変換することで、永続化と、トレーニングを同時に行ってしまいました。

ただ、ここで注意が必要なのが、「Memory ストレージエンジンでは、インデックスは基本 Hash となる」 ということです。
InnoDB から変換した際も同様、Hash インデックスとなるため、そのままでは範囲検索に利用できなくなり、遅くなってしまいます。

なので、 Memory ストレージエンジンに変換した後、現在のインデックスを DROP し、同じインデックスを BTREE で構築しました。

ALTER TABLE comment 
 DROP INDEX article, 
 DROP INDEX article_2, 
 ADD INDEX article USING BTREE (`article`,`id`), 
 ADD INDEX article_2 USING BTREE (`article`,`created_at`);

ただ、 Memory ストレージエンジンにすることで、効果があったのかは正直分かりません。
(アプリ書き換えの前に実行していたので、そのときは結構スコアが伸びたのですが、書き換えの後だと BufferSize を 適切に設定した InnoDB でもそんなに変わらなかったかも。)

とはいえ、トレーニング目的としては、手っ取り早く実装できたので、結果オーライ。

クエリキャッシュ

他のチームでは、結構初めの段階で Memcache を使ったキャッシュを考えていたようですが、一人しか居ないので、利用できるようになるまでに時間がかかりすぎると思ったので、もっと簡単にキャッシュを利用する方法を使いました。

MySQL のクエリキャッシュです。

query_cache_type = 1
query_cache_size = 64M

という設定を my.cnf に追加しました。

query log を出力するようにして一度ベンチマークを走らせると、ほとんどが似たクエリで、更新系が明らかに少ないことが分かっていたので使えるんじゃないかと。

更新系が多いと効果は薄くなります(ライトスルーキャッシュ的なことはできないので)が、
時間ないときにはおすすめのチューニング。

実際、これだけで結構スコアが伸びました。

その他ちょこちょこ

apache の MPM を Worker にしたり、使われていないモジュールを削除したり、ログを出力しないようにしたりと、細かい修正も入れました。
ただ、この辺は結構軽微で、ベンチ結果も誤差範囲だった感じです。

また、アプリケーションを node にして実行したときにもスコアが伸びたのですが、修正しづらかったので perl に戻しました。
(が、結局アプリの修正をほとんど行わなかったので node でも良かったかもしれません。)

アプリケーションサーバをもう一台増やせばもう少しスコア伸びたかも。

あまり計測してない

反省点としては、しっかりと計測していないこと。
ほとんどフィーリングと、グラフだけ見て判断してました。

やはり、現状をしっかりと把握できてたチームは強かったですね。
時間が少なかったからこそ一人でもそれなりのスコアが出せましたが、もっと時間があったら完全に詰まってました。一人で考えられる事って結構限界ガガガ。

最後に

「くまさんちーむ」 というのは、 先に登録されていた「黒猫さんちーむ」を見て、工画堂スタジオが浮かんだからという安直な理由です。それ以外特に意味はありません。

他のチームがどういうことをやったのか知りたいので、これ読んだ参加者の方は是非、なにをしたのかを書いてもらえると嬉しいです。

livedoor のスタッフの皆様、このような楽しいイベントを開催していただき、ありがとうございました。

リンク

なんでもありのWebアプリケーション高速化バトル、#isucon 開催のお知らせ
http://blog.livedoor.jp/techblog/archives/66528186.html
準備されていたアプリケーションとベンチマーク
https://github.com/tagomoris/isucon
iscon レギュレーション
http://www.slideshare.net/tagomoris/isucon-regulation
チート対策とhttp_loadに仕掛けた罠の話 #isucon
http://blog.nomadscafe.jp/2011/08/http-load-isucon.html
isucon終了に寄せて - tagomorisのメモ置き場
http://d.hatena.ne.jp/tagomoris/20110827/1314458440
#isucon で優勝してきました - 酒日記 はてな支店
http://d.hatena.ne.jp/sfujiwara/20110827/1314460582
#isucon ではどんなことを考えながら作業していたか - 酒日記 はてな支店
http://d.hatena.ne.jp/sfujiwara/20110829/1314597283
#isucon で優勝させてもらってきました - おそらくはそれさえも平凡な日々
http://www.songmu.jp/riji/archives/2011/08/isucon.html
isuconお遊びチーム(事前社内β組)の設定あれこれ - Perlとかmemoとか日記とか。
http://d.hatena.ne.jp/hideden/20110828/1314490713
#isucon に参加してきました。 - かるぱねるらすたいる
http://d.hatena.ne.jp/karupanerura/20110828/1314469620
それ、Gentooだとどうなる? - isuconに参加してきた&チーム「いんふらえんじにあー」の戦略など
http://d.hatena.ne.jp/tmatsuu/20110827/1314467819
#isucon に参加してきました&isuconツールを試してみました - As a Futurist…
http://blog.riywo.com/2011/08/28/070009
#isucon に参加してきた - 刺身☆ブーメランのはてなダイアリー
http://d.hatena.ne.jp/a666666/20110827/1314576816
#isuconの結果を反省 または私は如何にして心配するのを止めてパラメタ設定を細かくいじるることに終始したのか - MYFINDER'S BLOG
http://blog.myfinder.jp/2011/08/isucon.html

終結果はこれ?