ISUCON10に参加して予選敗退しました

公開:2020/09/22
更新:2020/09/22
4 min読了の目安(約2400字TECH技術記事

参加まで

だいぶ時間が経ってしまいましたがISUCON10の参加記録です。
試しに最近流行っているzennで投稿してみます。

昔の会社の同僚である@ikkuntech, @IshidallPと即日安心カードローンというチームで参加して最終スコア842点(参考値)で予選敗退でした。
ISUCONは前々から知ってはいましたが参加は初めてでした。
当日までは、ISUCON7, 9の予選の過去問をひとりで練習で解いて本番に向けて感覚を養っていたり、チームメイトとちょっとした情報共有するなどしていました。

役割としてはチームにインフラをメインにしている人がいなかったため、過去問で練習していてある程度勝手がわかっていた自分がインフラまわり、他2人がアプリまわりということになりました。

コミット履歴とか汚いですが下記リポジトリにコードがあります。
言語はGoです。

当日

うろ覚えですが、ある程度時系列で書いていきたいと思います。
Discordでやり取りをしていたのでそのメッセージをさかのぼったりしています。

12時20分頃開始

sshでサーバにログインできることを確認。
つづいて、サーバスペック、DBのテーブル情報、ブラウザでのアプリの確認など。
また、my.cnf, nginx.conf でログ、クエリキャッシュの設定など基本的な設定を追加しました。

14時 low_priced でインデックスが効くようにする

ベンチマークを動かして、MySQLのスロークエリとnginxのアクセスログをpt-query-digest, alpで解析して遅いクエリと時間がかかっているAPIなどを割り出しました。
price, rent でORDER BYするクエリが遅かったので、price, rent にインデックスを貼りました。

15時 検索系の改善

@ikkuntechが検索のカウント数のキャッシュなどをやってくれました。

16時 なぞって検索の改善に乗り出す

なぞって検索が遅いことがわかってきたので、@IshidallPに改善を依頼しました。
Spatial Indexを貼れば速くなるなどいいところまでいっていたのですが今一歩及ばず。
あとで復習して知ったのですがST_Containsにより範囲内検索を一度でできるのでN+1を外せるんですね。これはすごい。

16時 DBを別サーバに分けようとする

ほぼ同時にDBを別サーバに分ける作業を自分の方でやっていたのですがこれが大失敗。。
3306番ポートを空ける段階でよくわかりもせずufwを有効化して22番ポートを空けないままログアウトしてしまったので再度ssh接続できなくなり3台あったサーバを1台しか使用できなくするという痛恨のミスをしてしまいました。
本来であればmysqld.cnfに記載されているbind-address=127.0.0.1をコメントアウトしてMySQLを再起動するだけでよいのでufwなんて触らなくていいのですがやってしまいました。
(予選終了後のチャットを見ていると、他にも何チームかこのやらかしがあったようでした。)

17時 botによるアクセスをnginxで弾く

マニュアルに書いてあったbotのアクセスをnginxで弾きました。

18時 popularity DESC, id ASCでインデックスが効いてない問題の改善

スロークエリを見ていて降順と昇順ORDER BYを組み合わせるとインデックスが効かないことに気づいたのでなんとかインデックスを効くようにしました。
ただ、スマートな方法が思いつかず、id ASCをSQLから除き、取得したレコード情報を再度アプリでソートし直すという方法を取ってしまいました。
あとでGenerated Columnsで -popularity のカラムを作るという方法を見たときはなるほどそういう手があったかと膝を叩きました。

19時 もろもろ検索条件にインデックスを貼る

椅子検索、物件検索で使われそうなカラムにインデックスを貼ったりしました。
けっこう時間終了まで粘ってましたが、いいベンチが出るまでガチャを引くみたいなことをやってた気がします。

予選終了後に対応したもの

  • DBサーバをestateとchairで2台に分ける
  • なぞって検索のN+1改善とSpatial Indexの付与
  • CSVでのインサートをバルクインサートに変える
  • 検索の範囲指定を範囲インデックスに変えて検索させる
  • recommend_estateのORをなくしシンプルな検索に変更する
  • デプロイシェルの作成

感想

講評でもなぞって検索のN+1改善、DBサーバを適切に分けることが予選突破のターニングポイントとあったので、そこにある程度気づいていながらも知識、力不足でスコアを上げ切ることができなかったなあと思いました。
特に今回はpopularity_descカラム、なぞって検索でのPOINTカラム、検索範囲インデックスカラム、recommend_estate用の幅・高さ・奥行の最小値・中央値カラムの作成などGenerated Columnsが大活躍したので、これを知っていてうまく使いこなせたかそうでないかでは大きな差が出るなと感じました。
自分も知ってはいましたがこれまで実務で使ったことはなく思いつくことができませんでした。

その他、スクリプトによるスムーズなデプロイ、思いついた課題リストを整理しておくなどもうまくできていませんでしたが時間のない中でパフォーマンスを上げていく上では大切なことだなと実感しました。

運営のみなさん楽しい機会を作っていただきありがとうございました。
来年はもっと強くなってリベンジしたいと思います。