はじめに
社の同期 3 人の「スタンプラリー」チームで ISUCON10 予選に出場しました。 最終スコアは 1727 点で予選敗退しました。 50位くらいでした。 本戦出場ボーダーが2158点なので、あと1,2手というところでした。
やったこと(チームで)
予選前
ISUCON9 予選問題を 3 人で解きながら練習しました。 初動の動きを確認したり、よく ISUCON で使われる手法を練習しておきました。
- Git リポジトリの作成
- Makefile 作成
- デプロイスクリプト作成
- pt-query-digest, kataribe, pprof, htop で計測
- DB のインデックス
- N+1 クエリの解消
- IO 並列化
- オンメモリ化
- 複数台構成
などを練習しました。 もちろん、一人で全部できるようにするわけではなくざっくり役割分担をしました。
予選中:序盤
練習と同じで、MySQL と Nginx でホッとしました。
練習したことを着々と行いました。
Git リポジトリの作成、Makefile の作成、デプロイスクリプトの作成、pt-query-digest, kataribe, pprof の導入をしました。
計測すると、/nazotte
が重そうで見たことがない SQL が書いてあり🤔となりました。
また、/api/(estate|chair)/search
や/api/recommended_estate/:id
も重そうでした。
tips
Makefile を事前に作っておいたのですがとても便利です。
make setup
でシュッとインスタンスに分析ツールをインストールしたり、make slow
やmake kataru
するだけで自動で分析結果が slack に投稿されるようにしたり、make before
やmake restart
でシュッとベンチ前に準備したりできます。
予選中:中盤
/nazotte
の N+1 クエリの解消を行いました。
チームメンバーがアプリ内で与えられた多角形の中に点が含まれているか判定するアルゴリズムを書いてくれました。
なぞって機能、チームメンバが「進研ゼミで見たやつだ」って言って、アプリケーションで判別してn+1解消してくれた #isucon
— kotaroooo0 (@kotaroooo0) 2020年9月12日
Nginx で Bot を弾くのを地道に書きました。 設定ファイルでの書き場所や正規表現周りで若干ハマりました。
DB と APP のインスタンス分割もしました。 インスタンスを分離すると htop での計測が分かりやすくなって良いですね。
予選中:終盤
pprof 外し、MySQL, Nginx のチューニングを行いました。 練習ではあまり効果がでなくて期待せずに、行ったところ結構効果がでました。 メモリには余裕があったので、クエリキャッシュやバッファプールを潤沢に使いました。 これにより、スコア 1700 を超えてきてそこで時間もないのでやめました。
予選中:マージしなったもの供養
MySQL8 へのバージョンアップをやろうとしました。 降順インデックスが張れるらしく性能アップが期待されましたが、クエリキャッシュ機能がないため 5.7 よりパフォーマンスは上がりませんでした。 降順インデックスを貼りたいなら、ソート対象に -1 をかけるなどし反転してから昇順インデックスを貼ればよいというテクがあるようです。
物件をメモリ全載せしようとしました。
/estate/detail
が最も呼ばれておりこれを減らせば DB は軽くなるなと思いました。
更新処理ないから全部載せられるのではと思いましたがベンチは verify で落ちました。
終わってから気付きましたが、これしてたら再起動テスト通らなくなるし verify で落ちて良かったです。
再起動テストでは/initialize
は叩かれないみたいです。
サーチ結果をメモリキャッシュしようとしました。
/api/(estate|chair)/search
のクエリが重かったので、検索結果をメモリキャッシュしようとしました。
このエンドポイントでは、対象件数を取得する SQL を 1 回、そのあとページごとの対象物件を取得する SQL を 1 回実行していました。
ページごとの対象物件を取得する SQL は何度も叩かれることは少なそうかつキャッシュも難しそうだったので、対象件数を取得する SQL をキャッシュしようと思いました。
しかし、ベンチの verify で落ちました。
予選中:やったほうが良かったこと
アプリケーションのデバッグモードオフやログ出力オフにした方が良かったです。
今回は DB に 2 テーブルしかなく、なおかつ JOIN がなかったためテーブルごとにインスタンスを分けることができました。
なぜDB分割するの思いつかなかったのか、悔しいな #isucon
— kotaroooo0 (@kotaroooo0) 2020年9月12日
また、クエリを簡潔にするために事前に計算してカラム追加したりすると良かったようです。 例えば、物件検索でドアの高さとして指定できるのは 4 パターンしかないので DB にもその 4 パターンで保存したりします。
おわりに
問題があったときに打ち手をシュッとたくさん挙げられるようになりたいなと思いました。 DB の CPU 使用が 100%に張り付いてボトルネックになっているのは分かりましたが、クエリを減らすオンメモリ化を行うか、クエリを軽くする INDEX を貼るか、SELECT 文を最適化するかみたいな選択肢しか分かりませんでした。 DB を二つに分割する打ち手は考えられませんでした。 また、カラムを追加することによって高速化する打ち手も考えられませんでした。 DB に関しては、普段からインフラを触ってる人なら経験から思いつけたりするのかなあ。
それでも練習したことはできたし、ちゃんと ISUCON できたので満足でした。 次は決勝行くゾ。