nipeblog

解説みながらISUCON12予選でSQLiteのままRuby実装で8万点(予選No.1の水準)にいけた

2022年に開催されたISUCON12予選を解説をみながら試してみて、SQLiteのままRuby実装で86,910点(予選No.1の水準)までいけました。 どのような流れでスコアを高めていけたかと簡単にやったことを紹介します。

ISUCON12予選とは

ISUCONは、「Iikanjini Speed Up Contest」の略でWebアプリケーションのパフォーマンスを向上させてスコアを競う競技です。ISUCON12ということでいままで12回実施されています。2023年もISUCON13が予定されています。

ISUCON12関連の記事はこちらにまとまっています。

また、ISUCON12予選のスコア(参考値)は以下のようになっています。

予選突破相当のスコアは24,000点です。また、予選トップのスコアは86,740点になっています。

ISUCON12予選のスコア

ISUCON12予選でどのような流れでスコアを高めていけたか

大まかな流れとしては、次のとおりです。

  • 最初は、N+1やインデックスがささってない、pumaのworker数の調整など比較的簡単に対応ができ今後のボトルネックになりそうな部分を対応
  • 次に、ロックの取り方や時間がかるID生成など仕様やコードを確認しつつ対応が必要なことを改善
  • そして、最後にDBデータの圧縮やレコードを先に作成して読み込み時のレスポンスを高めるなどより手がかかるものを実施

注意点としては、必ずしも改善したからといってスコアは一辺倒にはあがっていきません。

理由としては、API同士の競合が起きていたり、マシンの負荷などのボトルネックがあるためです。

そのため、 alphtoppt-query-digestなどで計測し、スコアは変わらなくても狙った項目が改善されたかを確認しながら進めていくと良いでしょう。

ISUCON12予選でスコアをあげるためにやったこと

ISUCON12予選の場合は、競技マシンは3台あり、それぞれにNginx, Web Application、MySQL、SQLiteがそれぞれ配置されています。

ベンチマーカーから1つのサーバにリクエストが送られてくるので、ロードバランスやデータベースの共有などは自分で設定していく必要があります。

私は、ISUCON12 予選の解説 (Node.jsでSQLiteのまま10万点行く方法) を上からたどりながら実装していきました。

解説通りに1つずつパフォーマンスチューニングをすることで着実にスコアを上げることができました。

  • Rubyの場合の「初期スコアは1,921点」でした。
  • 1台でスコアをあげていくことで「29,124点(予選突破相当のスコア)」になりました。
  • そして、3台構成にすることで「 86,910点(予選トップ相当のスコア)」を達成できました。

1台構成でスコアをあげていく

  • 初期スコア(1,921点)
  • adminDB visit_history にINDEXをはる(1,665点)
  • Ranking APIが重いのでひとまずループクエリをなくす(1,768点)
  • Score APIの追加のループクエリをなくす(3,713点)
  • pumaのworkerを32、スレッドを1にする(6,681点)
  • アトミック書き込みのためのflockをトランザクションに変更する(7,483点)
  • dispenseIDでMySQLを使うのをやめる(7,012点)
  • adminDB visit_historyの初期データをコンパクトにする(6,636点)
  • ログのフラッシュを1秒ごとにする & バイナリログ無効化(6,377点)
  • Finish APIでBillingReportを生成する(14,738点)
  • tenantDB player_scoreにINDEXをはる(20,952点)
  • Ranking APIでランキング集計するのをやめる(26,802点)
  • Nginxワーカーコネクション数を3倍にする(29,124点)

1台から3台構成にする

  • 1台から3台構成にする(77,783点)
    • add tenant APIでtenant dbを作らない、代わりに、connect_to_dbでファイルがなければつくる
    • nginxのworker_rlimit_nofileを12000に設定。「socket() failed (24: Too many open files) while connecting to upstream」 を対応
    • 1台目のnginxでapi配下を2台目と3台目にホストの長さに応じて負荷分散する ※tenant dbがsqlite3なので単純なロードバランシングはできず
    • 2,3台目から1台目のMySQLに接続するようにする。また、1台目のMySQLはローカルのみしかアクセスできないのでフルオープンする
  • ログ出力をおさえ、何回かベンチマーク実行(86,910点)

詳細が気になる方は ISUCON12のチューニングメモ - gist にメモを書いていますのでご覧くださいませ。

まとめ

解説をみながらISUCON12予選でSQLiteのままRuby実装で8万点というスコアを出すことができました。

解説をみながらでも試行錯誤しつつ12時間ほどかかったので、これを本番の競技で実施できたトップチームの方々はすごいなと改めて思いました。

しかし、練習でできないことは本番ではできないので、少しずつ過去のISUCONにトライしていきたいです。🪑