ISUCON9 Finalのベンチマークを動かして遊んでみた
ISUCONとは
ISUCONとはWebアプリケーションの高速化を競うコンテストです。2011年から毎回開催されていまして、現在(2019年時点)で9回コンテストが開催されています。詳しい説明は公式ページがあるので、そちらを見ればよいかと思います。コンテストで動かすソースはgithubで公開されています。 https://github.com/isucon コンテストで扱う言語はgo, ruby, python, php, perlと良くWeb系で扱われる言語は含まれているようです。
ISUCON9 Finalの環境構築
ISUCON9の本選で扱われたソースは以下になります。 https://github.com/isucon/isucon9-final
環境はdockerで構築するようですが、ビルドにMakefileを使っており、また以下のベンチマークビルド用のMakefileを見るとMac, Linux向けでビルドを実行しているのが確認できます。 https://github.com/isucon/isucon9-final/blob/master/bench/Makefile
自分はWindowsの環境で確認しようと思ったのですが、そのままではうまく動かなそうだったのでHyper-v上のubuntuで環境を構築することにしました。
環境の構築方法はREADMEにのっていた以下のコマンドを実行しました。
git clone git@github.com:chibiegg/isucon9-final.git cd isucon9-final (cd webapp/frontend && make)
export LANGUAGE=go docker-compose -f webapp/docker-compose.yml -f webapp/docker-compose.${LANGUAGE}.yml build docker-compose -f webapp/docker-compose.yml -f webapp/docker-compose.${LANGUAGE}.yml up
これで以下のようにdockerが起動していることが確認できました。
~$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 262db77c105b phpmyadmin/phpmyadmin "/docker-entrypoint.…" 10 hours ago Up 31 minutes 0.0.0.0:8082->80/tcp webapp_phpmyadmin_1 7b5e3c09392a nginx:1.17 "nginx -g 'daemon of…" 10 hours ago Up 31 minutes 0.0.0.0:5000->5000/tcp, 0.0.0.0:8080->80/tcp webapp_nginx_1 901cc941f8e7 webapp_webapp "go run main.go util…" 10 hours ago Up 31 minutes 127.0.0.1:8000->8000/tcp webapp_webapp_1 1d80f0847562 mysql:8 "docker-entrypoint.s…" 10 hours ago Up 31 minutes 33060/tcp, 0.0.0.0:13306->3306/tcp webapp_mysql_1 7ab922f48244 golang:1.12 "go run main.go" 10 hours ago Up 31 minutes webapp_payment_1
次にREADMEREADMEにのっている通り以下のURLにアクセスしフロントエンドを表示し、それからログインするためのユーザ登録を実行してみました。
フロントエンドのページは表示できたのですが、ユーザの登録時にMySQL周りでエラーが発生することが確認できました。
docker-compose.ymlのMySQL周りの設定を確認してみたところ、MySQLのデータは mysql:/var/lib/mysql
となっておりdockerで共通のvolumeを使っていることが確認でき、./sql:/docker-entrypoint-initdb.d
とあるのでソース上にあるSQLを/docker-entrypoint-initdb.d
にマウントし、 ./mysql/conf.d:/etc/mysql/conf.d
でMySQLの設定ファイルをマウントしていることが確認できます。
mysql: image: mysql:8 command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci environment: - "TZ=Asia/Tokyo" env_file: - ".env" volumes: - mysql:/var/lib/mysql - ./sql:/docker-entrypoint-initdb.d - ./mysql/conf.d:/etc/mysql/conf.d # development only ports: - "13306:3306"
気になるのは ./sql
のディレクトリ内にあるSQLが実行されているかですが、以下のように直接確認しに行ってもDBが作られていなかったので初期化SQLが実行されていなかったようです。
$ docker exec -it MySQLのコンポーネントID mysql -uroot -proot mysql: [Warning] Using a password on the command line interface can be insecure. Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 97 Server version: 8.0.18 MySQL Community Server - GPL Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 4 rows in set (0.02 sec)
DBおよびユーザの作成が必要なのですが、以下のソース内では環境変数がなければデフォルトの設定を使うようになっているようで、初期では特に環境変数も指定されていないのでデフォルトのDB、ユーザで接続できるようにしておきます。
user := os.Getenv("MYSQL_USER") if user == "" { user = "isutrain" } dbname := os.Getenv("MYSQL_DATABASE") if dbname == "" { dbname = "isutrain" } password := os.Getenv("MYSQL_PASSWORD") if password == "" { password = "isutrain" }
それからwebapp/sql内のSQLを実行したら、MySQL周りのエラーが発生しなくなりユーザの登録ができるようになったことが確認できました。
ベンチマークの実行
次にベンチマークを動かしてみます。READMEにのっている通り、以下のコマンドを実行します。
cd isucon9-final (cd bench && make)
それから、ubuntuの環境で動かしていたので以下のコマンドでベンチマークを実行します。
bench/bin/bench_linux run --payment=http://localhost:5000 --target=http://localhost:8080 --assetdir=webapp/frontend/dist
結果は以下になり、初期では1316のスコアになりました。
{"pass":true,"score":1316,"messages":["GET /api/train/seats: リクエストに失敗しました (タイムアウトしました)","POST /api/auth/signup: リクエストに失敗しました (タイムアウトしました)","エンドポイント成功回数: 298","スコア: 1331","ペナルティ: 15"],"available_days":10,"language":"golang"}
ここからパフォーマンスを改善することでスコアを伸ばせるのですが、公式のページの方に解説がありますので、これを参考に修正していけばよいかと思います。
GET /api/train/searchの改善
解説の記事では列車の検索や空席の検索でクエリの実行回数が多いとのことと、あとインデックスを張る場合はorder byを適切に指定する必要があるとのことだったのでそれらを踏まえて修正しました。 github.com 結果として初期コストの1300くらいから2500くらいまでは上がりました。
dockerでの環境構築の部分を含めてコンテストのソースを見るのは慣れてないと大変ですが勉強になりそうで、動くところまで行ったら解説をもとに修正するのが面白くなりそうでした。また、普段触らない言語であってもサンプルの動くコードを見ることで覚えるきっかけによさそうでした。