💺
ISUCON12 やったこと
ISUCON12 予選に出場したのでやったことのまとめ。ほぼ自分用のメモです。
まだ追記予定。
準備でやったこと
-
Cloud Formation Template を適用してリソースを構築
-
SSH 用の config ファイル(以下) 作成
~/.ssh/configHost i1 HostName XX.XX.XX.XX User isucon Host i2 HostName XX.XX.XX.XX User isucon Host i3 HostName XX.XX.XX.XX User isucon
-
事前準備していた Ansible を全サーバに適用
- alp
- bash_it
- percona_toolkit (pt-query-digest)
-
作業ファイルを Git 化
- GitHub
- webapp フォルダ以下
- Nginx config
- DB サーバローカル Git
- /etc/mysql 以下
- GitHub
-
各サーバで使用しないサービス停止
sudo systemctl stop XXX sudo systemctl disable XXX
-
MySQL スロークエリ有効化
-
alp 設定
- Nginx ログフォーマット形成
- alp ログまとめコマンド形成
-
notify_slack インストール
改善でやったこと
-
MySQL サーバーをアプリケーションサーバから分離
-
index
-
GET /api/player/competition/:competition_id/ranking の SELECT tenant に LIMIT 1 つけた
-
dispenseID 関数の ID 生成をアプリのみでやるように変更
-
visit_history 周りの改善
-
事前に visit_history テーブルから visit_history2 テーブルを作成するようにした
CREATE TABLE `visit_history2` AS (SELECT `player_id`, `tenant_id`, `competition_id`, MIN(`created_at`) AS `created_at`, MAX(`updated_at`) AS `updated_at` from `visit_history` GROUP BY `player_id`, `tenant_id`, `competition_id`); ALTER TABLE `visit_history2` ADD PRIMARY KEY (`competition_id`, `tenant_id`, `player_id`);
-
アプリケーションコードで visit_history 使っていた部分を visit_history2 に置き換え。GROUP BY など不要になった。
-
-
sqlite3 を MySQL に以降
- sqlite3 ダンプファイルを csv に変換
for i in `seq 1 100` do sqlite3 -header -csv initial_data/$i.db "select * from competition;" > output/competition_$i.csv sqlite3 -header -csv initial_data/$i.db "select * from player;" > output/player_$i.csv sqlite3 -header -csv initial_data/$i.db "select * from player_score;" > output/player_score_$i.csv echo $i.db; done
- Python で クエリ作成(以下サンプル)
competition schema = "INSERT INTO `competition` (`id`, `tenant_id`, `title`, `finished_at`, `created_at`, `updated_at`) VALUES " with open("sql/competition.sql", "w") as w: for i in range(100): with open("output/competition_" + str(i+1) + ".csv") as f: lines = f.readlines() count = 0 for l in lines: if count == 0: count += 1 continue ls = l.split(",") res = '' # print(ls[0], ls[1], ls[2].rstrip('"').lstrip('"'), ls[3], ls[4], ls[5]); if ls[3] == '': res = schema + "('{0}', {1}, '{2}', NULL, {4}, {5});\n".format(ls[0], ls[1], ls[2].rstrip('"').lstrip('"'), ls[3], ls[4], ls[5].rstrip('\n')) else : res = schema + "('{0}', {1}, '{2}', {3}, {4}, {5});\n".format(ls[0], ls[1], ls[2].rstrip('"').lstrip('"'), ls[3], ls[4], ls[5].rstrip('\n')) w.writelines(res)
-
出力した SQL をMySQL 初期化シェルに追加
-
player_score の行数が多く、初期化に時間がかかりすぎるので player_score は tenant_id, player_id, competition_id が同じデータの中で row_num が最大のもののみを残す
-
MySQL 移行に伴いアプリケーションコード書き換え
-
POST /api/organizer/competition/:competition_id/score でバルクインサート
できなかったこと
- /api/player/player/ を 3 台目に割り振る
- Nginx, カーネルパラメータをチューニングする
反省点
- MySQL をダンプしてベンチ実行のたびにデータを初期化しなければならなかった
Discussion