isucon #2へ参加してきました

前回非常に楽しかったので、今回も参加したいなーと思っていたのですが、「くらげとみかん」チームで、なんとか参加できました。

走り書きだけど、考えたこと、やったことをメモっておく(あとでまとめなおすかもしれない)

まずは最初に全サーバーへsshの鍵を通して、screenで全サーバーへログインした。
前回ベンチマークを走らせるのが遅かった、という反省があったので、まずベンチマークを実行した。

まずappをいじるための環境を整備した。git reposを作ったり。(11:30)

次は、アプリケーションの概要を把握するために、全tableのschemaをshow create tableしたり、全テーブルの件数を把握して、stockテーブル以外は対した容量にならない、ということを把握した。

long_query_timeを0にして、ベンチを走らせて、全クエリをslowログへ出力し、mysqldumpslowで解析した。(この時点でpt-query-digest使っておけばよかった...)

前回は、ボトルネック以外のところへ時間を消費してしまった、という反省があったので、stockテーブルへ注力してチューニングを始めた。

チケットの席を発行する処理がぱっとみ遅そうだ、ということで、mysqldのINSERTまわりの設定をチューニング。

具体的には、

  • innodb_flush_log_at_trx_commitを0に。
  • log-binを無効に

してみると少しスコアがあがった。(通常のwebサービスでは真似しないように)

alter table stock change column seat_id seat_id varchar(6) ascii binary not null;

してseat_idのデータを減らす。

deployスクリプトをかいてなかったので、appをいじるためにdeployスクリプトを書く ( 13:00ごろ)

"order by rand"しているクエリがあったので、とりあえずオフにしてベンチしてみたら通ったので、そのままにした。

alter table stock ROW_FORMAT=Compressed

してみたら少しあがった。

mysqldumpslowが見づらいので、やっぱりpt-query-digestを落としてきて実行する。

stockテーブルのupdated_atは、最後のチケットCSVをダウンロードしているページでしか参照していなくて、order_requestにあっても同じ、ということでupdated_atを移動してやる (stockテーブルよりorder_requestの方が行数がすくない)

alter table order_request add column `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

alter table stock drop column updated_at

updateやJOINしているクエリで読む行数を減らせないかな、というので、

CREATE TABLE `stock_part` (
  `variation_id` int(10) unsigned NOT NULL,
  `seat_id` varchar(6) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  `order_id` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY `variation_seat` (`variation_id`,`seat_id`)
) ENGINE=InnoDB AUTO_INCREMENT=40961 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED
PARTITION BY LINEAR HASH(variation_id)
PARTITIONS 10;
rename table stock to stock_bk, stock_part to stock;

してみた (14:43)

stockテーブルの"order_id IS NULL "や、"order_id IS NOT NULL"のクエリが重いので、
そこをなんとかしてなくせないかなーと考えていたら、updated_atと同じ容量で、order_requestへ移せばいいんじゃね?と思ったので、アプリケーションを安全に保ちつつ、徐々に移行していった。

具体的には、variation_id/seat_idをorder_requestへ追加して、二重にINSERTするようにした。

次はJOINしまくっているクエリで、stockテーブルを使っていたところをカラムを追加したことでorder_requestを代用できるようになったのでとても高速になった。 (16:12)

最後にCSVを出力する処理でもstockを使わずにorder_requestで返せるようになったので修正 (16:20 )

このごろになると、徐々にチューニングが効きはじめて、最初のボトルネックからはかわってきたので、slow queryを取得し、pt-query-digestを実行し、アプリを書きかえては、ベンチを実行して測る、というのを繰り返した

for文の中で、1クエリ実行している箇所があったため、INを使ったクエリへ書きかえた。 (17:00)

さらにfor文の中で、1クエリ実行している箇所があったのでINを使って書きかえた(17:16)

チケットの残り件数を表示している処理がボトルネックになっていたので、これを高速に返すために、チケットを売りあげたときにカウンタテーブルを更新し、チケットの残り件数は、
全チケット数 - 売りあげたチケット数、で算出する、という方式に変更
。(17:45)

ここまででタイムアップしました。

最終的にはチケットの売上枚数が少なかった(1887 ticket)ですが、スコア(260461, BEST: 235274)はけっこうよかったようです。
おそらくカウンタテーブルを用意したことで、購入処理は重くなったが、その一方でチケット残数表示は早くなったんじゃないかと思います。

他の方のまとめはこちら

http://blog.livedoor.jp/techblog/archives/67726806.html