木を見て森を見て林を見て

LUCK値で生き延びる

ISUCON10予選に初参加してみた(予選敗退)

3行まとめ

  • 会社の研修でISUCONをやったが全然できず、kotaroooo0、kaneko656を誘って本番に再チャレンジ
  • 最終スコアは1700ぐらい、予選突破ならず
  • 練習してた部分はできたかな思いつつも、振り返ってみるともう少し改善できたなと思った

はじめに

始まりは会社の研修でISUCONをしたこと。会社の研修のISUCONではまったくパフォーマンスを出せずに悲しい気持ちなった。悔しいので本番で再チャレンジしたいなと思い、kotarooo0とkaneko656を誘って参加した。

利用言語はGo、1か月前ぐらいから一週間に1回集まって練習を行い、利用ツールや役割分担などを決めた。

参加にあたって利用したツール

ツール 用途
Slack pt-query-digest、kataribeの結果を流す用
Discord ボイスチャット、チャット用
Google meets 画面共有用

役割分担

役割
sshayshi(自分) DB、インフラ担当
kotaroooo0 アプリケーション改善担当
kaneko656 アプリケーション改善担当

ISUCON10参加中の脳内

俺だけsshできないんですが 12:20 - 14:00

レギュレーション読み、アプリケーションコード・configをgit管理、計測に使うツール導入などの初期セットアップ行った。

開始時、自分だけアプリケーションインスタンスssh接続できず困惑した。原因は自分のGitHubアカウントには鍵が2個登録されており、古いPC(すでに手元にない)で利用していた鍵が踏み台サーバに置かれていたようだった(現在使っている鍵ではsshできなかった)。幸いにも昔のPCのバックアップディスクから鍵を取得、無事インスタンスにログインできた。(ファインプレー1)

余談:開始時間が12:00から12:20になったとき、他のメンバーと競技用サービス名の予想をしていた。isuruitでは?等と話していたらisuumoだった。惜しい。

スコア:500

これ!進研ゼミでやったやつだ! 14:00 - 16:00

全員でkataribeを使ってボトルネック確認し、改善に取り組んだ。

/api/estate/nazotte、/api/estate/search、/api/estateあたりがmean、totalが大きくボトルネックになっているのがわかった。kotarooo0が検索系のapiをキャッシュ化できないかトライした。自分はpt-query-digestを使って遅いクエリを確認し、index貼りをトライした。kaneko656氏はnazotteAPIのN+1クエリが改善にトライした。

nazotteAPI、クエリ上で何をやっているのか自分にはまったくわからなかったが、kaneko656氏は見覚えがあったらしく(?)「これ進研ゼミで見たやつだ!」感を出しながらN+1を改善してしまう。(ファインプレー2)。その後goのプロファイリングツール(pprof)の導入も行ってくれた。

自分はexplainで確認しながらDBにindex貼っていく。スコアは少し伸びたものの、order by desc, ascとなっているクエリにインデックスが効かない。MySQL8.0から使える降順インデックス貼れば改善できそうと気付く。

余談:N+1が改善された後、みんなで動作確認のためにnazotteAPIをアプリケーション上で使ってみたところ、普通に使いやすくて実際のsuumoにも欲しいなと思った。追記:Web版にはないのでWeb版にほしいという話

スコア:500 → 750

MySQL8.0に裏切られる 16:00 - 17:00

降順インデックスを使うために、MySQL8.0の導入を試みた。「MySQL8.0導入時にアプリケーション動かなくなるとまずいのでは?」というkaneko656氏からの声により、別インスタンスにてMySQL8.0を導入してみる。無事アプリケーションが動かなくなる。原因は、my.cnfが5.7のときに使っていたものをそのまま利用していたことだった。query_cashe等がMySQL8.0ではconfに書けなくなっていたのでコメントアウトして無事8.0を動かすことに成功した。

しかし、MySQL8.0でベンチを走らせるとスコアが300~350程度になってしまい困惑した。「MySQL8.0のconfのチューニングができていないのかな…」と仮説を立ててみるも原因がわからない。このままだと降順インデックスを貼ったとしても5.7よりスコアが高くなるかかわからなかった。事前にhtopでリソース使用量を確認し、DBがCPUリソースをとりまくっていたのは知っていたので、確実にスコアが伸ばせるDBとアプリケーションの分離を行うことに決めた。

kaneko656氏、kotaroooo0氏はレギュレーションを読んでBotの存在に気付く。Nginxでbotを弾くことにトライしてくれた。

スコア:750

練習通りDBインスタンスを分けることに成功 17:00 - 18:00

インスタンス001(Nginx、App)、インスタンス002(MySQL5.7)という構成を作った。ここでスコアは900ぐらいになる。インスタンス003(MySQL8.0)は後々の改善用として用意した。

二台構成(001、002)でベンチを回すも、DBインスタンスでCPUが100%に張り付いており、DBがボトルネックになっているのを確認した。自分は投げられてくるクエリの負荷を下げることを目的に、kaneko氏達はそもそも投げるクエリの数を減らすことを目的に動く。

kaneko656氏、kotarooo0氏はNginxでbotを弾けるようにしてくれた。これのおかげでスコア少し増加した。

スコア:750 → 950

さよならMySQL8.0 19:00 - 20:00

MySQL8.0で降順インデックスを貼り、無事slow-queryの多くを改善できた。ベンチ実行時、メモリ使用量に余裕があったのでinnodb_buffer_pool_sizeをチューニングした。その結果、MySQL8.0のDBインスタンスを使った場合1200ぐらいのスコアが出るようになる。お、こちらでいけるか?と思ったりした。 一方、kotaroooo0氏「5.7のMySQLinnodb_buffer_pool_sizeチューニングしてないよな」と気付きチューニングしてくれる。チューニング後にベンチを回すと1500ぐらいのスコアを出す(ファインプレー3)。この時点で8.0を使うのを断念した。

スコア:950 → 1500

再起動試験(仮)20:00 - 21:00

残り時間1時間になったので、再起動試験のチェックしないと!と思い、全インスタンスを再起動する。その後ベンチを回して無事スコアが出て、「ヨシッ!」となっていた。

※これはsshayashiが再起動試験を勘違いしており、本来はベンチを回す前にアプリケーションが正常に動いている必要がある。

再起動試験(仮)を無事終えたところで残り30分、kotaroooo0氏、kaneko656氏がNginxの細かいチューニングを入れたりログを切ったりしてくれたおかげでスコア1700ぐらいになる。これでもういいだろうという感じで終了。

スコア:1500 → 1700

Discordで感想戦 21:00 - 21:30

isucon10終了後、Discordのgeneralチャンネルで色んな方々の感想を3人で通話しながら眺めていくと学びや共感がたくさんあって楽しかった。

共感パート

  • MySQL8.0入れるとスコア半分になって悲しかった
  • DBの負荷を追いかけていたが改善しきれなかった
  • nazotteのN+1を改善するの難しかった
  • 一位チームの7000点は一体何をしたんだ…

学びパート

  • MySQL8.0はquery cacheがないので5.7よりパフォーマンスが出ない(知らなかった)
  • MySQL8.0ではデフォルトでバイナリログの出力がOnになっていててパフォーマンスが下がる(知らなかった)
  • joinが行われていないのでそれぞれを別DBインスタンスに分ければよかった(思いつかなかった)
  • elastic searchで頑張ろうとした(思いつかなかった)
  • partial indexを使った(知らなかった)
  • 降順インデックスを使わなくても、idとpopularityを1つのカラムに纒めてインデックス使った(プロか?)
  • (popularity = -popularity)のカラムを使って昇順に合わせた(プロか?)

参加後の反省

反省ポイントは大きく2つある。

1つ目はMySQL8.0の利用に囚われ、MySQL5.7を利用していたインスタンスに改善できる場所を残してしまったことである。 pt-query-digestで出力したslow-queryの内、上の方しか見ていなかったのが原因だった。上の方にあった遅いクエリはdesc, ascの組み合わせで遅くなっていたものであり、それらが直せないのであれば、別のクエリを改善すればよかった。

2つ目はもう少しアプリケーションコードを読めばよかった、ということである。今回のISUCONはDBへの負荷をいかに分散・改善させるかが重要だったように思える。自分は「すでに投げられているクエリ」の改善に注目しすぎていた。他の参加者のブログなどを見ていると、そもそも投げる必要のないクエリがいくつかあったようだ。もう少しアプリケーションコードを読んでいれば、そういったクエリを削減できたかもしれない。

まとめ

反省すべき点はいくつかあるものの、今回は本質的な部分で悩めたのが良かった。というのも、研修でISUCONを行ったときはNginxの元のconfをコピーし忘れるなどのミスで、本質的な部分に取り組めなかった。今回は大枠としては計測と改善を繰り返し、どうすればアプリケーションがより早くなるかを考えることができた。とても楽しかった。運営さんに感謝。

おまけの記念画像

f:id:sshayashi0208:20200913193804p:plain
25位以内に入ってるときにスクショした