ISUCON5 の予選にメルカリのインフラ系エンジニアで結成したチーム「GoBold」で参加して、無事2位で通過しました。チームのメンバーは、@cubicdaiya、@shmorimo、@kazeburoの3人で、普段から横にならんで座って、メルカリのパフォーマンス改善やサーバ環境の整備に携わっています。
今回の予選問題、ずいぶん盛ってきたなーというのが最初の印象。モリスさんも予選問題を本選のようにしたいと言っていたし、その通りになっていました。マシンの性能に対しての扱うデータ量もメモリに乗り切らないであろうスレスレのラインでkamipoさんの調整も神だなと思いました。おかげでやることが満載だし、素晴らしい問題でした。運営の941さんも含め本当にお疲れさまでした。本選もよろしくお願いします。
チームのメンバーのblog
ISUCON5予選 2位で通過しました - 考える人、コードを書く人
やったこと
最終的なソースコードといくつかの設定はgithubで公開しました
https://github.com/kazeburo/isucon5-elimination-public
構成は、Nignx + Perl + MySQL + memcachedです。PHPは使ってないです。はい
課題となったサイトは、ISUxiというSNSサイト、日記、日記のコメント、足跡、そしてhome.plのようなトップ画面。元m社のたんぽぽチームとアプリ運用チームの2人いるチームとしては負けてはならない課題でした。結果はfujiwara組に負けましたが。。あーくやし
やったこととスコアを時系列に整理してみると、こんな感じ。全部で72回ベンチマークを実行。
time score 11:22:12 52 <rubyの初期スコア 11:43:41 557 <perlの初期スコア 12:30:23 818 12:55:18 1121 <userのrequest単位のcache、パスワードのhashの計算をperlで行うように 13:05:18 0 13:08:44 0 13:18:33 0 13:22:56 19 13:25:04 0 13:26:37 794 <ようやくsuccess 13:30:04 882 13:34:47 1191 13:37:15 1191 13:41:08 1223 13:43:06 1198 13:46:40 0 13:48:00 0 13:52:11 916 13:54:03 19 13:55:23 19 13:59:30 1883 <is_friendsがmemcachedとなる, commentsテーブルにentry_user_idカラムを追加 14:02:33 0 14:04:29 1967 14:07:46 1926 14:10:31 1947 14:22:49 4 14:26:09 2144 <userをin memory化 14:29:41 2137 14:38:35 20 14:41:18 2160 14:48:39 1088 <enqueueが2つされる 14:48:39 1117 14:51:26 2182 15:06:01 17 15:13:24 2026 15:18:41 4277 <comments_of_friendsのクエリ改善と足跡の集計をperlで行うように 15:23:25 4305 15:25:50 4242 15:29:41 4372 15:32:21 4376 15:36:00 7134 <relationsのクエリからOR anotherを削除 15:40:29 0 15:44:02 8166 <comments_of_friendsのクエリ改善。limit 1000がなくなる 15:49:47 18439 <entries_of_friends,comments_of_friendsのクエリ改善、relationsをつかう 15:53:24 18873 15:56:05 18561 16:00:03 18167 16:07:13 19525 16:11:47 22126 <nginxのoptimize、unix domain socketを使ったり 16:18:35 21333 16:22:58 21378 16:28:49 18497 <再起動。このあたりから再起動とwarmupの試験 16:45:34 16823 16:51:06 0 17:06:42 17623 17:10:21 19950 17:13:38 20102 17:28:07 18107 17:37:25 17 17:44:09 16217 17:49:53 14297 17:55:28 1571 <友達のいないユーザからのアクセスでエラー多発 18:00:17 0 18:17:52 0 18:20:51 21243 < warmupをcatで行うようになって再起動後もスコア安定 18:27:54 21622 18:30:27 22719 18:34:52 22845 18:39:46 23553 <comments_of_friends limitの調整 18:46:30 24118 18:53:47 26300 < comment数のN+1を改善 19:00:00 25861 < 最後の再起動試験
MySQLのクエリ数が多いので、そこを減らすことから取りかかり、relationsのmemcached化とuserのin memory化を先に進め、その後に比較的重いクエリをひとつひとつ解決していきました。fujiwara組と逆かも。
アプリケーションのコード変更は3人で話し合い、次に解決する課題を整理し @shmorimo と自分とで分担して作業を行っていきました。直接masterにpushしていたけど、p-rベースにしてコードの確認をやっていた方が手戻りが少なかったかなと思う。
MySQLの設定とwarmup
今回のデータは3GB近くあり、それに対してサーバのメモリが3.5GB、HDDはSSDではなく超遅いというのが特徴。そこで再起動試験で安定したスコアを出そうしたら、warmupが重要になる(ならなかったけど)ということで、いくつか工夫をしました。
my.cnfは以下
innodb_buffer_pool_size = 800M innodb_flush_log_at_trx_commit = 0 innodb_flush_method=nosync innodb_doublewrite = 0
bufferpoolを減らして、ODIRECTは使っていません。innodbのbuffer_poolだけに頼るのではなくdisk cacheで行う戦略をとりました。まるでMyISAMのようですね。
そしてアプリケーションの起動スクリプトには
/bin/cat /var/lib/mysql/isucon5q/*.ibd > /dev/null exec /home/isucon/.local/perl/bin/carton exec -- start_server --path /tmp/app.sock --backlog 16384 -- plackup -s Gazelle --workers=10 --max-reqs-per-child 500000 --min-reqs-per-child 400000 -E production -a app.psgi
のように書いて、メモリに乗っけてからアプリケーションを起動するようにしました。おかげでアプリケーションサーバさえ起動していれば満足のいくスコアがでるようになっています。
反省
22000がでたところで、安心してしまいGo Boldに行かなかったところが反省。アプリケーションを変更するプロセスもよくしていって、本選は(fujiwara組に)勝ちたい。