普段はJavaを使っているので、Javaの参照実装を使ってISUCON 12予選を解いてみました。
実施環境・条件#
- 環境: AWS EC2
- 競技環境: c5.large *3
- ベンチマーカー: c5.xlarge
- 条件
- 制限時間は設けない
- 実装言語はJava
- ベンチマーカーの管理者向けログは極力見ない (
./bench (args) | grep -v ADMIN
) - ベンチマーカーの
-reproduce
フラグは指定しない ( 予選当日の挙動を再現しない)
リポジトリ・ベンチマーク記録#
- リポジトリ: https://github.com/maruTA-bis5/isucon-practice/commits/isucon12-qualify-java
- ベンチマーク記録: https://github.com/maruTA-bis5/isucon-practice/issues/2
やったこと#
- Javaの参考実装に切り替えてベンチ:
SCORE: 421 (+934 -513(55%))
ログ- Goと比べてスコアは低め。エラーもかなり出ている状況でした
- docker剥がし:
SCORE: 432 (+960 -528(55%))
ログ - ID採番をアプリ側で実施:
SCORE: 426 (+1251 -825(66%))
ログvisit_history
のSELECTのほうが重い状況だったので、この段階で採番を変えてもあまり伸びませんでしたね
- sqlite3からMySQLへのデータ移行
- 予選当日はやらなかった、データ移行に挑戦しました。最終的に、tenandId=1の
player_score
以外はすべてMySQL側に移行して、tenanId=1は特別扱いすることに。 player_score
テーブルでは、必要なのは最新のレコードだけなので、最新のレコードだけをCSVに吐き出して移行しています ( https://github.com/maruTA-bis5/isucon-practice/commit/f6043a09b0490b5a900709a72af5ee080dcff178)- CSVに吐き出し終わる前にコードを修正していたので、しばらくベンチがfailする状況が続いています
- 予選当日はやらなかった、データ移行に挑戦しました。最終的に、tenandId=1の
- プレイヤーのスコアは最新のみ保持するようにし、バルクインサート化
public.pem
はBeanの初期化時に1度だけ読み込んで、それを使い回すvisit_history
テーブルにインデックスを作成- 終了した大会の請求情報を
competition_billing
テーブル(新規)に保存するように - billing: 終了していない大会は集計せずに即座に返却する
- ヒープメモリ割り当ての調整
- ここで調整ミスって、EC2インスタンスのメモリを食いつぶしてしまったので、再起動しつつ3号機へ作業環境を移行しています。まだ1台構成ですね
- デッドロック時にretryするように:
SCORE: 4913 (+4913 0(0%))
ログ - App/DBでCPUを取り合っている状況なので、DBを1号機に移行:
SCORE: 10215 (+10423 -208(2%))
ログ competition_billing
の更新を、競技終了後に2号機で行うように- 各種N+1の解消:
SCORE: 31317 (+31956 -639(2%))
ログ - Nginxに若干負荷がかかっていたので、余裕のある2号機へ移動:
SCORE: 33548 (+33886 -338(1%))
ログ - 各種監視・ログを止めて最終スコア:
SCORE: 32551 (+38295 -5744(15%))
ログ
最終的な構成#
- 1号機: MySQL
- 2号機: Nginx, App
- 3号機: App
感想#
flock
の代わりにsynchronized
を使っている点がJava実装固有の差異でしたが、それ以外はGoと同様だったので、改めて実装面では言語による有利・不利が少なくなるようにされているんだと実感しました。- 最終スコアは
32551
だったので、やるべきことを時間内にやれていれば本戦出場も狙えそうだ、という印象ですね。あとは手を早く、正確に動かせるように練習が必要。