isucon練習記 isucon9(予選) 2度目
もはや覚えてないので実質初見
環境構築
code:いつもの
$ sudo hostnamectl set-hostname isu1
$ sudo mkdir -p /home/isucon/.ssh &&\
sudo cp ~/.ssh/authorized_keys /home/isucon/.ssh &&\
sudo chown -R isucon:isucon /home/isucon/.ssh
証明書が必要っぽいので作る
ベンチ回す
code:bench
./bin/benchmarker \
-allowed-ips ${isu1のIP} \
-target-host isu1.mkizka.dev \
外部APIはベンチマーカー起動時にベンチ側で動作するタイプっぽい
Goの初期スコア{"pass":true,"score":2310,"campaign":0,"language":"Go","messages":[]}'
Nodeの初期スコア{"pass":true,"score":1110,"campaign":0,"language":"nodejs","messages":[]}
ひくい
マニュアル
チューニング
練習がてら前回使い忘れたCloud Profilerを入れてみる
gcloud入れる
アプリにエージェント入れる
code:profiler.js
// ファイルの先頭に
require('@google-cloud/profiler').start({
projectId: '${プロジェクトID}',
serviceContext: {
service: '${適当な名前}',
version: '1.0.0',
},
});
サービスアカウントにログイン(たぶん必要)
こんな感じになった
https://i.imgur.com/A6coMBF.png
終盤、どうしても負荷を下げる方針が見つからなくなった時に見たい
pt、kataribeなど環境整備
目立つのはこのへん
GET /items/*.json HTTP/1.1
GET /users/transactions.json HTTP/1.1
GET /new_items/*.json?created_at=*&item_id=* HTTP/1.1
まず/users/transactions.jsonみる
明らかなN+1あり
カテゴリは追加も削除もないみたいなのでキャッシュしてみる
{"pass":true,"score":1210,"campaign":0,"language":"nodejs","messages":[]}
ptからSELECT categoriesが消えた
ユーザーもトランザクション中は変化ないだろうし先に全取得してみる
{"pass":true,"score":1010,"campaign":0,"language":"nodejs","messages":[]}
SELECT usersの呼び出しが24000→22000くらいになった
感触がよくないのでptを見る
1位のクエリはGET /new_items/*.jsonのクエリっぽい
効きそうなインデックスを適当に足してみる
INDEX idx_new_category_items (status, category_id, created_at)
{"pass":true,"score":1010,"campaign":0,"language":"nodejs","messages":[]}
正直なんでこれが有効なのか全くわからないけどpt上でのクエリの順位が1位から4位に落ちた
ベンチマーク回しながらhtop見るとCPUにかなり余裕がある
余裕ありすぎてスペック間違えた?t2.mediumだけど
キャンペーンを1にしてみる
{"pass":true,"score":4900,"campaign":1,"language":"nodejs","messages":[]}
キャンペーンを2にしてみる
{"pass":true,"score":5010,"campaign":2,"language":"nodejs","messages":[]}
1が限界っぽい
当日のスコア分布チラ見したらいきなり5000点いってるチームほとんどいなくて環境差異が原因かも
またptを見るとELECT * FROM items WHERE id = ? FOR UPDATE\Gが1位
6箇所使われてるけどkataribe的にPOST /buyっぽい。Totalも一位だし改善の余地あり
内容を見る
(1)購入商品をロック
(2)購入商品で販売者をロック
(3)取引履歴を「発送待ち」で作成
(4)商品に購入者、「取引中」状態、更新時間、を登録
(5)Shipmentから集荷予約を作成(initial)
(6)Paymentから決済を実行
(2)(3)(4)同時に出来たりしない?意味なさそう
access.logを見ると取引中の商品を買おうとしてるやつが死ぬほどいる
新着一覧とかに取引中のやつが出ないようにするべき?
新着とカテゴリ新着から商品を一つ消してみたけど、普通に購入が来る
方針がブレてきてよく分からなくなってきたのでマシンスペックをc5.largeに変えて実質最初からやり直してみる
解説もチラ見しながらやる
素直にインデックス貼りから始める
{"pass":true,"score":1110,"campaign":0,"language":"nodejs","messages":[]}
カテゴリをキャッシュ
{"pass":true,"score":1210,"campaign":0,"language":"nodejs","messages":[]}
ループ中のgetUserSimpleByIDを全部剥がす
{"pass":true,"score":1210,"campaign":0,"language":"nodejs","messages":[]}
24587回呼ばれてたSELECT usersが2978回になった
代わりにSELECT * FROM usersが増えてしまったのでWHEREで絞る
{"pass":true,"score":1410,"campaign":0,"language":"nodejs","messages":[]}
UNION句のやつやる
{"pass":true,"score":1210,"campaign":0,"language":"nodejs","messages":[]}