ISUCON5 の予選1日目にチーム「fujiwara組」(@fujiwara, @songmu, @sugyan) として参加して、全体通して1位のスコアで通過しました。
今回は ISUCON 1 の時の優勝チームを再結成という形になったわけですが、最初はISUCON 4の時と同じ社内のチームででようかと思ってたんですよね。ところが昨年優勝チームだった「LINE選抜 生ハム原木」が今回参戦できないということで、sugyanがチームどうしよう、と困っていたのでつい…*1
初代fujiwara組を再結成しよう
— fujiwara (@fujiwara) 2015, 5月 27
準備
今回はOSは Ubuntu(バージョン非公開)なのが事前にレギュレーションで公開されていたので(前年まではCentOS, Amazon LinuxなどのRedHat系ディストリビューションでした)、これはきっとWebアプリケーションの起動を systemd でやりたいんだろうなあと予測をして、Ubuntu 14.04LTS, 14.10, 15.04で最低限の初期設定とオペレーションができるように Chef cookbook を整備しておきました。
といっても、使いそうなパッケージをあらかじめ入れておくのと、各自のアカウント設定 (ユーザを作成、sudoできるようにする、githubの公開鍵をauthorized_keysに設置する) ぐらいです。kernelパラメータのチューニングなどは特に何もしていません。また、以下のツールも便利なので Chef でインストールするようにしておきました。
あとは作業用 Slack と github のプライベートリポジトリを用意ですね。
当日
メンバー全員の所属がばらばらなので、検討の結果、はてなさんの表参道オフィスの会議室を借りました。LINEさんのカフェも検討しましたが、おそらく参加者多数になるので落ち着かないのと、窓際のソファー席を確保できない場合椅子に不安が…ということで。
準備整ってる #isucon pic.twitter.com/IRUEnLP9Hq
— fujiwara (@fujiwara) 2015, 9月 26
写真に写っているサブディスプレイは On-Lap のもので、現行品だと以下のようなやつになるでしょうか。持ち運びに便利なので ISUCON 本選でも活躍します。おすすめです。
13.3インチモバイル液晶モニター On-Lap 1303H
- 出版社/メーカー: GeChic Corporation
- 発売日: 2014/12/05
- メディア: Personal Computers
- この商品を含むブログ (1件) を見る
やったこと
スコアの推移は以下のようになりました。
timestamp score 11:41:25 305 12:03:33 0 FAIL: 12:08:14 587 12:18:27 1333 < indexを2個足した (fujiwara) 12:24:22 0 FAIL: 12:27:22 1888 < my.cnf調整(fujiwara) 12:31:27 1672 12:52:11 2008 < Gazelleに入れ替え (fujiwara) 13:47:38 2832 < entriesからtitleカラムを分離(sugyan, fujiwara) 13:49:24 2867 13:52:20 5206 < entriesから1000件取ってるのをなくす(sugyan) 13:58:35 5500 14:03:53 9353 < relationsから or を削った(songmu) 14:37:46 9593 15:05:11 20 FAIL: 15:08:00 10009 15:15:17 10092 15:25:29 10137 15:30:49 0 FAIL: 15:36:53 10138 16:27:23 11247 < comments_for_meをredisのlistに(sugyan) 16:43:50 17 FAIL: 16:48:45 17 FAIL: 16:58:10 17 FAIL: 17:04:20 17 FAIL: 17:39:28 12389 < footprintをredisのsorted setに(songmu) 17:47:06 0 FAIL: 18:01:09 17039 < userをmysqlではなくredisから読む(sugyan)、3回相手の属性調べてたのを一発に(songmu) 18:11:22 16402 18:16:07 18193 < / でコメント10件毎回entriesを取得していたのをwhere inに(songmu) 18:23:54 26338 < redisから引いたuserをプロセスのメモリにcache(sugyan) 18:30:44 26153 < initializeでaofからredisを初期化(fujiwara) 18:40:11 26694 18:42:59 27232 < 再起動試験後の最終提出スコア
基本的な作業の流れとしては
- 自分がログ (主に 0.01 sec閾値で出力したMySQLのslow query logとalpでの集計結果) をみながら改善ポイントを指摘
- songmu, sugyanがコードを読んで効率が悪いところを発見
- 3人で対応方針を検討
- songmu, sugyan両名がそれぞれ並列でコード修正
- その間 fujiwara はインフラ周りの細かいこと(あんまりやることなかった)
- 修正が終わったものをmergeしてベンチ
をひたすら繰り返していく、という感じでした。 途中 footprint の Redis 化の実装が難航して苦しい時間帯もありましたが、基本的には手戻りなしで一直線にスコアを上げていけたのでいい流れでしたね。
entriesからtitleカラムを分離
entries.bodyが巨大でtitleを表示するためだけに取得するのが重かったので、セオリー通りにカラムを分離しようとしました。ところが1.8GBあるentriesテーブルのALTERが、diskのスループットが全然でなくてなかなか終わらず。(1MB/secぐらいしかでてなかった)
結局snapshotからSSDインスタンスを用意してそっちで作業して戻す、ということをして乗り切りました。
alter table entries add title varchar(191) not null default ''; UPDATE entries SET title=SUBSTRING_INDEX(body, '\n', 1);
- もういちどsnapshotを取得
- Standard diskのインスタンスで起動
/initializeでのRedisデータ初期化
一部のデータはRedisに移す実装をしています。
ベンチ開始時に /initialize にアクセスが来て、そこで初期データ以外のデータは削除されるのですが、そのタイミングでRedisのデータも整合性を取って初期化する必要があります。
そこで普通にMySQLを読んでRedisに書き込みをすると30秒の時間制限を超えてしまうので、MySQLの初期データに対応するRedisの初期データセットを作った上で redis-cli config set appendoonly yes
にしてaofファイルを吐き出しておき、initalize時にはそれを redis-cli で読み込んで一気にロードしました。2秒で終わります。
$redis->flushall(); system("/usr/bin/redis-cli --pipe < /home/isucon/appendonly.aof") if -e "/home/isucon/appendonly.aof";
感想
とにかく問題のボリュームが大きくてやることがいろいろあって、リモートベンチも快適に掛かって、tagomorisさんが「これがISUCONだ、という予選にしたい」といっていたのが実現できていて素晴らしい出題だったと思います。 運営の皆様、本当にありがとうございました。
本選でも3年ぶり3回目の優勝を勝ち取れるように頑張りたいと思いますので、よろしくお願いします!
*1:昨年のチームに不満があったとかでは全くないのであしからず……