ISUCONに出た
「初めて蟹を見た kofuk」というふざけた名前のチームで @uta8a と ISUCON13 に出ました。
当日のリポジトリも公開しています。
パフォーマンスに詳しくない人がほとんど準備せずに行くとこういう景色になるという感じで、結果は褒められたものではなかったです。 ただ ISUCON は人それぞれのレベルで頑張れるイベントで知識が薄くても暇になるということはないという感じで、良いイベントだなーと思いました。 めちゃくちゃ楽しかったです。
ちなみにチーム名はこれ↓が元ネタです。
蟹を初めて見た上田晋也みたいなツイートを見て電車で大笑いしていてマジで恥ずかしい
— kofuk (@man_2_fork) April 14, 2023
uta8a が詳しくブログに書いてくれました。
追記
最終的な結果が出ていて、最終的なスコアは 11602 だったらしいです。
本番前まで
申し込みと Rust でやると決める以外の準備を驚くほどしませんでした。
ひとりでやったこととしては、達人が教えるWebパフォーマンスチューニング〜ISUCONから学ぶ高速化の実践を一通りはやりました。 あと、ISUCON 用にやったという感じではないんですが、SQLクックブックとかいう本をペラペラ眺めていたのが役に立ったような立たなかったような気がしています。 本番っぽい練習は private-isu を達人が教えるやつを見て写経したのと、数日前に KOBA789 さんの ISUNARABE で去年の予選のサーバを立てて絶望したりしました。
チームでの打ち合わせは前日までやっていなくて(適当すぎる)金曜日の夜に慌てて ISUNARABE を立ててゴニョゴニョやったりしましたが、できたのは初手にサーバの内容を GitHub に上げるときの打ち合わせという感じでした。 でも僕はそのあたりを考えてなかったので事前に確認できて良かったです。
本番
問題
ライブ配信サイト?という題材でした。 誰かがライブ配信しているところにコメントとかいいねとか投げ銭ができたり、ランキングがあったりしたり、あとはコメントの NG ワードを設定したりといったことができる Web アプリでした。
ライブ配信という題材ですが、動画のストリーミングはチューニング対象ではなく、その周辺に生えている API がチューニング対象でした。
本番での動き
まずは前日の打ち合わせ通りにサーバの内容を GitHub に上げました。
その後、alp
と pt-query-digest
を使ってボトルネックを探して修正していくという感じになりました。僕はほぼ見てないんですが、uta8a が top でベンチマーク中のリソースを監視してくれました。(名前忘れたけど監視系のアプリで定番なやつがあったような。やらなかったけど)
僕が主にボトルネックとして認識していたのが N+1 問題とかインデックスが貼られてないとか画像が DB に入っているとかだったので、なんとなくそのへんを潰していく感じになりました。 でもここは後で振り返ると視野が狭かったです。もうちょっとじっくり分析してやることを考えられると良かったなと思います。
まず、ランキングの API に分かりやすく N+1 になっているクエリがあったのでそこを潰しました。 もともと全ユーザのスコアを取ってきてランキングを出す、という実装になっていたんですが、ライブの ID を与えたらクエリ一発でランキングが出るようにしました。 今考えるとこれはやり過ぎでした。ここで頑張ってクエリを書いていた時間を使って他の部分のチューニングができたと思いますし、実際に MySQL 側では N+1 にならない程度に軽く JOIN する程度にしてアプリ側で集計して返すのでも十分速かった雰囲気があります。 ちなみにこれができた時点で昼を過ぎていた気がします。
次にやったのがスロークエリのログを見てインデックスを貼るというやつです。 目立って遅かったのがタグ(ハッシュタグ的な意味でのタグ)を探してくるというやつでした。 インデックスを貼るとかなりスコアが上がり(当社比)、気持ち良かったです。 でも後で聞いた話だとタグは固定なのでそもそも DB に入れなくて良かったらしいです。
次にやったのが MySQL に画像が入っているのを静的なファイルにするというやつです。 画像を保存する部分は uta8a がやってくれて、僕は GET の方をやりました。 画像は初期化時に全消しされることに気づかず、DB から画像を取り出してファイル化する努力をやってしまいました。 ここで思いの外苦労してしまったのと、Nginx の設定でハマりました。
ここでまたタグの N+1 に気づいたのでクエリ一発にしました。ハードコーディングでいいやつなので本来無駄だったみたいなんですが、スコアは微増しました。
最後微妙に時間があったので別の最初にやった N+1 解消を別のランキング API に横展開しました。 激ヤバクエリをいじくり回していたら奇跡的に動いたので良かったです。 ここでスコアが 11167 とかになりました。
最後にログとかを外して終わりにしました。 最終的なスコアは覚えてないです。
気づき
準備が足りなかったとかは置いといて、本番での気づきというか反省点です。
- 初手でサーバを分割すると良かったかもしれない
- ちゃんとコードを読んでいなかった
- 既存のコードの改善という発想で動いてしまっていたなと思います。もうちょっと挙動を分析できればシンプルな方法になったりもっと速くなったりしたんじゃないかと思っています
- ローカルで MySQL を立てて検証できるようにしようとしてしまった
- 一瞬で諦めたんですが、最初からやらなくて良い動きだった感はあります
- サーバで手動のオペレーションが多かった
- ログの分析とかログを消し飛ばしたりとかをスクリプトにしたのも後半に入ってからで効率悪かったです。このあたりは事前に整備しておく部分かもしれない
- SQL まわりにしか目がいかなかった
- DNS 水攻め攻撃されていたらしいんですが、気づきませんでした