mattsu6666 / isucon4 Goto Github PK
View Code? Open in Web Editor NEWThis project forked from isucon/isucon4
This project forked from isucon/isucon4
Webアプリ部分のprofilingをし, 上層から全体のどれに時間がかかっているかを特定
使うツールはこちら https://github.com/google/pprof
/login
にリクエストが来ると,有効なユーザかどうかをチェックするためにMySQLを参照しているが,新規ユーザの登録やユーザ情報の変更は発生しないので,全てメモリ上で管理する
db.go#getCurrentUser
のロジックをメモリから参照するように置き換えるfunc getCurrentUser(userId interface{}) *User {
user := &User{}
row := db.QueryRow(
"SELECT id, login, password_hash, salt FROM users WHERE id = ?",
userId,
)
err := row.Scan(&user.ID, &user.Login, &user.PasswordHash, &user.Salt)
if err != nil {
return nil
}
return user
}
MySQLの参照をメモリに切り替えることでパフォーマンスを改善を見込めるが,重いクエリではないので,インパクトは少ないかもしれない
アプリケーションをGCPにデプロイし, 環境依存しないようにする。
GCPが一年間無料だそうなのでインスタンスを立てる
インスタンス1:アプリケーション・サーバー用の1コア1GBのインスタンス.
インスタンス2:ベンチマークサーバー(スペックは検討).
ベンチマークサーバーとアプリケーションサーバーのリージョンは分けたほうが良い?
理由はこの2つが同じリージョンであるとネットワークが強固すぎてgzip等の圧縮の効果が消えるため
BANされたIPを判定するロジックの効率が悪いため,改善したい.
また,どのように効率が悪いかを示す
IPBanThreshold
以上)の場合はBANとして扱うBANかどうかの判定をするためのクエリにインデックスが効いてない上,クエリ自体が鈍足.BAN情報をわざわざログから判別するのも良くない
SELECT COUNT(1) AS failures FROM login_log WHERE ip = ? AND id > IFNULL((select id from login_log where ip = ? AND succeeded = 1 ORDER BY id DESC LIMIT 1), 0);
(pprof) top1000
Showing nodes accounting for 14.36s, 81.50% of 17.62s total
Dropped 577 nodes (cum <= 0.09s)
flat flat% sum% cum cum%
4.22s 23.95% 23.95% 4.45s 25.26% syscall.Syscall
3.28s 18.62% 42.57% 3.28s 18.62% runtime._ExternalCode
1.49s 8.46% 51.02% 1.49s 8.46% runtime.futex
0.95s 5.39% 56.41% 0.95s 5.39% runtime.usleep
0.54s 3.06% 59.48% 0.84s 4.77% syscall.Syscall6
mysql> explain SELECT COUNT(1) AS failures FROM login_log WHERE ip = "127.250.0.247" AND id > IFNULL((select id from login_log where ip = "127.250.0.247" AND succeeded = 1 ORDER BY id DESC LIMIT 1), 0);
+----+-------------+-----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+-------+---------------+---------+---------+------+------+-------------+
| 1 | PRIMARY | login_log | range | PRIMARY | PRIMARY | 8 | NULL | 57 | Using where |
| 2 | SUBQUERY | login_log | index | NULL | PRIMARY | 8 | NULL | 1 | Using where |
+----+-------------+-----------+-------+---------------+---------+---------+------+------+-------------+
2 rows in set (0.00 sec)
Redisを使用し,IP毎の最新のログイン失敗回数を記録する
失敗回数を知るために,Redisに問い合わせるだけで良いため,効率が良い
BANされていることを示すカラムをusersテーブルに追加する.
BANされているかどうかはアプリ側の設定値にてコントロールする必要があるため,あまり実用的ではないが,ISUCONにおいては既定値は変わらないので有効
Lockされたユーザを判定するロジックの効率が悪いことを示す
概ね#6 と同等の問題を抱えており,同等の方法で解決可能と思われる
https://gist.github.com/koudaiii/386eb55a29b1adc19c5e
こちらの記事を参考にしてnginxの設定を良くしてみる
/login
にて有効なusernameおよびパスワードを受け取ったにもかかわらず/を返してしまっている問題.
/mypage
を見るためにはattemptLogin
にてErrorが発生しないことが条件である。
今回はattemptLoginにて正しいユーザーにもかかわらずこれらのエラーが起きていることが要因であると考えられる.
Error
が発生する条件は以下の通り
Banされた: ErrBannedIP
ロックされた: ErrLockedUser
Passwordの違う: ErrWrongPassword
loginNameの存在しない: ErrUserNotFound
上記の事から以下のことが考えられる
どのエラーが発生しているかを確認し、事象を捉える.
mysqlのクエリ速度改善, 同期的処理の追加
nginxのconfを設定する
worker process の数などをautoに設定
Githubからのデプロイや自動ベンチマークができると理想
最悪,手動でやればいいので優先度は普通
login_logの出力は同期実行されているため,効率が悪いと考えられる.
とはいえ,ログ書き込み程度はそんなに問題にならなそうなので,とりあえずラベルはlowにしといた.
attemptLogin
の実行終了時にcreateLoginLog
を実行し,ログインログを出力するdefer func() {
createLoginLog(succeeded, remoteAddr, loginName, user)
}()
#6 で示される方法を実施すれば,login_logをMySQLに書き込む必要が無くなりそう
go
修飾子をつけて別スレッドで実行するusersテーブルの構成とそれに対するクエリを見直す.
login
カラムを使った検索をしているが,該当カラムにはインデックスがなく,
さらに文字列で検索しているためパフォーマンスがでない原因と思われる
(pprof) top1000
Showing nodes accounting for 14.36s, 81.50% of 17.62s total
Dropped 577 nodes (cum <= 0.09s)
flat flat% sum% cum cum%
4.22s 23.95% 23.95% 4.45s 25.26% syscall.Syscall
3.28s 18.62% 42.57% 3.28s 18.62% runtime._ExternalCode
1.49s 8.46% 51.02% 1.49s 8.46% runtime.futex
0.95s 5.39% 56.41% 0.95s 5.39% runtime.usleep
0.54s 3.06% 59.48% 0.84s 4.77% syscall.Syscall6
mysql> EXPLAIN SELECT id, login, password_hash, salt FROM users WHERE login = "isucon1";
+----+-------------+-------+-------+---------------+-------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+-------+---------+-------+------+-------+
| 1 | SIMPLE | users | const | login | login | 767 | const | 1 | |
+----+-------------+-------+-------+---------------+-------+---------+-------+------+-------+
1 row in set (0.00 sec)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.