mizdra's blog

ぽよぐらみんぐ

ISUCON 12 予選に出場した

id:odan3240 さんと「情報処理部」というチームで出場してきました。

id:odan3240 さんの参加記もあるので合わせてどうぞ:

odan3240.hatenablog.com

結果

  • 最終スコア: 3270

何もできなかった...

時系列

結構前の話なのでうろ覚えだけど…大体こういう流れだったと思う。

  • 10:00 競技開始
  • 10:30 インスタント立てたりマニュアル読み合わせしたり
  • 〜11:30 ソースコード取り出したり、デプロイスクリプト書いたり、Datadog 入れたり
  • 11:30〜
    • id:odan3240 N+1 修正、インデックス貼る
    • id:mizdra sqlite から mysql に移行し始める
  • 14:00
  • 15:00
  • 17:30
    • Redis 導入も mysql 移行も間に合わない!となって、監視設定切ったりパラメータチューニングしてベンチマーク回し始める
    • 3270 点出たところで終了

やったこと

github.com

  • sqlite のスキーマに index を貼った
    • ...のだけど、途中でやっぱり sqlite 脱出しよう!となって中断して、実際に貼ったのは 1 つだけ。
  • sqlite のデータを mysql に移行する (失敗)
    • 最初 sqlite3-to-sql を使って移行を始めたけど、40分くらい? (よく覚えていない) 掛かる上に、完走した後に player_score テーブルのレコード数を数えたら、20万件くらい欠けていることが判明した
      • 虚無へと消えるレコード...
      • 警告とかエラーとかも出てなくて、何が起きていたのかさっぱり分からなかった
      • 「なんで〜」となって試行錯誤しているうちに 1 時間くらい溶けていた
    • そもそも 40 分掛かるのおかしいよね、ということで CSV に変換してから import する方法に切り替え始めた
      • for file in `ls ../../initial_data/*.db`; do
          sqlite3 -csv $file "select * from competition;" >> competition.csv
          sqlite3 -csv $file "select * from player;" >> player.csv
          sqlite3 -csv $file "select * from player_score;" >> player_score.csv
        done
        mysql -uisucon -pisucon -Disuports --enable-local-infile -e"load data local infile 'competition.csv' into table competition fields terminated by ',' OPTIONALLY ENCLOSED BY '\"';";
        mysql -uisucon -pisucon -Disuports --enable-local-infile -e"load data local infile 'player.csv' into table player fields terminated by ',' OPTIONALLY ENCLOSED BY '\"';";
        mysql -uisucon -pisucon -Disuports --enable-local-infile -e"load data local infile 'player_score.csv' into table player_score fields terminated by ',' OPTIONALLY ENCLOSED BY '\"';";
        
      • これで 20 分くらいに短縮された
      • けどレコードが虚無へと消えていくのは直らない...
    • mysql のバッファに余裕がなくてレコードが欠けてしまっているのでは? と思って、mysql のパラメータチューニングをし始める
      • [mysqld]
        innodb_buffer_pool_size=4G
        innodb_buffer_pool_instances=4
        innodb_log_buffer_size=200MB
        innodb_log_file_size=400M
        innodb_flush_method=O_DIRECT
        max_allowed_packet    = 64M
        
      • これで 3 分くらいに短縮された
      • レコードが虚無へと消えることもなくなった!
    • しかしベンチマークを回してみると POST /initialize がタイムアウトしてしまう!
      • webapp/sql/init.sh にこういう初期化用の sql を書いていたのだけど、created_at レコードに index がなくて、そもそも webapp/sql/init.sh に時間がかかりすぎるらしい
      • index 貼った
    • まだタイムアウトする!
      • GET /api/admin/tenants/billing が遅すぎてタイムアウトしてた
      • N+1 直してなかったのが原因っぽかった
      • 直そう、と思ったけど残り 30 分ほどしかなくて、ここであえなく時間切れ
      • https://github.com/odanado/isucon12-qualifier/pull/9
  • その他 import 中の待ち時間に nginx 周りでなにかできることないか探したり、シュッとできそうだったので /api/me をゲストユーザの時は nginx から返すようにしたりしたけど、ほとんど点数は変わらなかった

感想

sqlite to mysql に手を出したのが良くなかった… 危険な匂いを嗅ぎ分けて、先に他のボトルネック (それこそ最後に詰まっていた N+1 とか) から 1 つずつ潰していけばよかった。あとデータ量が多い時の mysql のパラメータチューニングの方法とかも事前に練習しておけばもっとスムーズだったかも。というよりは、業務でデータ量多い DB を扱う経験を積んだりとか、普段から色々なことに手を伸ばしていきましょうという話っぽい。もっと挑戦していきたい。

来年もがんばります。

ポケットモンスター・ポケモン・Pokémon・は任天堂・クリーチャーズ・ゲームフリークの登録商標です.

当ブログは @mizdra 個人により運営されており, 株式会社ポケモン及びその関連会社とは一切関係ありません.