なまえは まだ ない

思いついたことをアウトプットします

ISUCON14延長戦の記録⑥ MySQLサーバーを別インスタンスにする

ISUCON14の延長戦をやってます

以下の記事の続きです。

furusax0621.hatenablog.com

前回はISUCON14の目玉のひとつ(?)であるマッチングアルゴリズムの改善をしました。 これまでの改善も含め、スコアを一気に17,000点まで伸ばすことができました。

なお、最終的なコードは以下のリポジトリで公開しています。

github.com

やることがなくなってきた

マッチングアルゴリズムの改善まで済ませたところで、次にやるべきことが思いつかなくなりました。 自身の発想力のなさに嫌気が指します。

とりあえず苦し紛れに入れた改善を紹介します。スコアはわずかですが伸び、18,700点程度になりました。

インデックスの追加

改善の糸口を見つけるためにスロークエリログを眺めていたところ、クーポンを検索するクエリでインデックスが効いてないことに気付きました。 クーポンはユーザー増加によって使われる機会が増える機能です。初期実装では他の箇所がボトルネックになっていたことで、このクエリが上位に出てこなかったのでしょう。

追加したインデックスは以下のPull Requestで確認できます。

github.com

ライドの最新ステータスをテーブル管理する

ライドのステータスはride_statusesテーブルで管理されています。以前改善した椅子の位置情報と同様、履歴テーブルとして実装されています。アプリケーションの各所でこのテーブルから最新のステータスを取得する関数が呼ばれており、N+1問題になっている箇所もありました。

そこでライド毎の最新ステータスのみを管理するride_latest_statusesテーブルを追加し、ridesテーブルとJOINすることでライドの情報と最新ステータスを一度に取得できるようにしました。

なお、このテーブルについてもライドの初期データの情報を復元する必要がありますが、初期データの中身を覗いたところ登録されるライドはすべて完了済みになっていそうでした。 本来であれば椅子の位置情報と同様にride_statusesテーブルを時系列順に取得してUPSERTをかけるべきですが、ここは一定サボって固定で COMPLETED を挿入しています。

完全な差分は以下のPull Requestを参照してください。

github.com

MySQLサーバーを別インスタンスに分離する

そろそろ一台で色々やりくりする時期も終わったかなと判断し、ここでMySQLサーバーを別インスタンスに切り出すことにしました。 アプリケーションコード的には env.sh に記載されているホストを書き換えるだけで良いです。以下のPull Requestのとおりです。

github.com

※ なぜかInitializeエンドポイントがコケるようになったので、ライドの最新ステータスの挿入をUPSERTにしています

以下、MySQLサーバーを分離するためにやったコード変更以外の作業を紹介します。

インスタンスからのアクセスを許可する

MySQLサーバーがネットワーク経由で接続を受け付けるには、いくつかの設定が必要です。

まずはMySQLユーザーのホスト指定です。MySQLユーザーにはユーザー名の他にホストの概念があり、このホストが一致しないと例えユーザー名とパスワードが一致してもログインできません。 MySQLにrootユーザーでアクセスし、 mysql.user テーブルの情報を確認しましょう。ISUCON14の環境では、 sudo mysql でrootユーザーとしてログインすることが可能でした。

SELECT host, user FROM mysql.user;

ここでアプリケーションで利用しているユーザーのホストが localhost などになっている場合、他のホストからアクセスできるようユーザーを作成するなどの作業が必要です。幸いISUCON14では % (任意のホスト)が指定されていたため、この作業は不要でした。

次はMySQLサーバーが通信を受け付けるIPアドレス帯の指定です。 /etc/mysql.my.cnf/etc/mysql/mysql.conf.d/mysqld.cnf ファイルを確認し、 bind-address パラメータの値を確認します。MySQLのデフォルトは 127.0.0.1 (ローカルホストのみ許可)になっています。一番手軽なのは、これを 0.0.0.0 (任意のホスト)に書き換えることです。

[mysqld]
bind-address = 0.0.0.0

各種サービスの設定

MySQLサーバーを動かすインスタンスでは、アプリケーションやNginxサーバーが稼働している必要はありません。 不要なサービスは停止させ、CPUやメモリのリソースを有効活用できるようにしましょう。

sudo systemctl stop isuride-go.service
sudo systemctl disable isuride-go.service
sudo systemctl stop nginx.service
sudo systemctl disable nginx.service

また、スロークエリログを収集するためにpproteinエージェントを起動します。 競技序盤ではアプリケーションコードにpproteinエージェントを統合することを想定し、template-isucon-recipeはpproteinエージェントのサービスはインストールのみ行い、起動しないようになっています。 この先の改善のヒントを探すためにも、pproteinエージェントを起動しておきます。

sudo systemctl start pprotein-agent.service
sudo systemctl enable pprotein-agent.service

まとめ・次回予告

MySQLサーバーを分離したところ、さらにスコアが伸びて28,000点近くまで到達しました。本戦最終スコア換算でいうと25位に相当します。すごい、上位入賞だ!

スロークエリログの出力をオフにするなど細かいチューニングまで含めると33,500点まで届きました。本戦最終スコア換算で14位相当、特別賞を受賞できるラインです。

ここからもうひと伸びしたいなぁ〜と、再び講評を読んでヒントを探っていきます。つづく。