re:annkara

日々学んだことを書き留めておく。

ISUCON10 振り返り

少し時間が経ってしまいましたが、ISUCON10にチームBanBanジーとして参加しました。
最終スコアは参考スコアとなりますが、1048ポイントでフィニッシュでした。

SSHで時間を溶かす

今回、踏み台サーバ経由で与えられたサーバ環境に接続する必要がありました。
当日配布されたマニュアルを参考にssh.configを編集してたのですが、SSH力が低すぎて1時間ほど溶かしてしまいました。

ssh.configやローカルポートフォワーディングなど、実務ではほとんど触ったことがなかったので、ちゃんと理解しておきたいと思いました。

初手

最初の方でやったことです。

  • ドキュメント読み合わせ
  • 導線確認
  • サーバ・システム構成の確認
  • 初期ベンチマークの取得
ドキュメント読み合わせ

去年の反省も含めて当日ドキュメントをちゃんと読み込みしようと思ったのですが、前述のSSH周りの件で詰まってしまったのでちゃんと読めなかったです。

本当であれば、Bot周りの話やベンチマークの挙動、スコア計算周りはちゃんと理解しておくべきだったと思います。

導線確認

導線確認ではどんなアプリケーションなのかざっと把握するためにブラウザから実際にアクセスしていました。

今回は出題者の方がRecruitということもあって、SUUMOをモチーフにしたアプリケーションでした。なんとなく、ここらへんで更新系の処理が少なそうで参照系が多そうだなぁという印象を持っていました。

サーバ・システム構成の確認

稼働しているプロセスの確認とサーバリソースの確認をしました。

nginx - アプリケーション - MySQL が稼働しており、CPUは1コア・メモリが2GBほどでなかなかにシビアなサーバリソースでした。

初期ベンチマークの取得の取得

ある程度どんな環境なのか把握できた段階で初期ベンチマークの取得を行いました。

ベンチマーク実行中にはTopコマンドでリソース使用率の傾向を見ていましたが、CPU使用率が90%以上であり、かつMySQLのプロセスが支配的であったのでこれはDB周りがボトルネックになっているんだろうなぁというのがこの時点での感想でした。

今となってはですが、初期ベンチマークを取得する前にアクセスログ周りの設定やツールの整備、スロークエリログの解析準備を行ってから初期ベンチマークを取得すればよかったと思います。

やったこと

ざっくりと。

  • ツールの導入(alp/go_one)
  • nginx のアクセスログをLTSVに変更
  • スロークエリログの解析
  • index の追加
  • サーバ構成の変更
  • 実装の変更
  • nginxにキャッシュの設定を追加
index の追加

スロークエリログを見ながらインデックスを張ったりしてました。
とりあえずスコアは微増しましたが、適当すぎたのでちゃんとSQLを学ぼうと思いました。

ALTER TABLE isuumo.chair ADD INDEX idx_popularity(popularity);
ALTER TABLE isuumo.estate ADD INDEX idx_latitude(latitude);
ALTER TABLE isuumo.estate ADD INDEX idx_longitude(longitude);
ALTER TABLE isuumo.estate ADD INDEX idx_popularity(popularity);
ALTER TABLE isuumo.estate ADD INDEX idx_rent(rent);

サーバ構成の変更

MySQLサーバのCPU使用率が高いことがわかっていたのでDBサーバを分離しました。

最終的には nginx - アプリケーション - MySQL を1台ずつで構成する形になりましたが、nginx/アプリケーションのCPU使用率的にはほとんどボトルネックはなかったので1台のサーバでよかったと思います。
DBサーバを分離する案は出ていたのですが、実際にやったことがないということもあって手を出せずにいました。

後々、他の参加者のブログを見てたりすると、分割することでスコアが上がったという記事を見かけてたりするので、チャレンジしても良かったなと後悔しています。

余談ですがMySQLを分割するときにユーザごとに接続可能IPアドレスというものがあることを知らなくて、別サーバに分割するときにだいぶ苦労しました。

実装の変更

アクセスログからAPIの実装で直せる場所がないか探していました。

/api/estate/low_priced と /api/chair/low_pricedのAPIがnazotte検索や検索系APIに次いで、トータルの呼び出し回数・処理時間が多そうだったので実装を見てました。

実装としては都度同じクエリを実行して同じ結果を返却するように見えたので、オンメモリ上にキャッシュするように実装を変更しました。
トータルの処理時間としては1/60くらいにすることができましたが、スコア的には微増だったような気がします。

nginxにキャッシュの設定を追加

/api/estate/search と /api/chair/search APIの呼び出し回数と処理時間が一番多かったため、このAPIをどうにかしようとしてました。

実装をちゃんと変更できればよかったんですが、実力不足のためなんとか他の手段でできないかと検討してました。

コードをざっと見た感じ、クエリパラメータからクエリ実行用のパラメータを取得してSQLを発行しているように見えたので、安易に同じクエリパラメータであればその結果をnginx側から返却すればMySQLの負荷が減るんじゃないかと考えました。

最終的には上手くいったように見えたんですけど、ベンチマークの結果が安定しなかったり、/api/chair/search も同様のキャッシュの設定を入れると必ずFAILしたりと綱渡り感が半端ない感じになってしまいました。

ちゃんと実装を理解していればたぶん取らない手法だと思うんですが、結果としてこの修正をすることで1000オーバすることができました。

振り返って

去年よりもだいぶできることが増えたなと感じるとともに、まだまだできることがあるというのも実感できました。

今までなんとなく知識としてなんとなく知っていたものを、実際のサービスに活かせるようにするという点で実力も経験も足らないのだと改めて実感することができました。

このような機会を与えてくださったISUCONの運営の皆様に感謝いたします。また来年参加したいと思います。本当にありがとうございました。