当日やることメモ - isucon-koala/private-isu GitHub Wiki
困った時は ISUCON Cheat Sheet を見る!
スタートガイド
Github
下記リンクに従ってssh-addを行い、各自のGithub用の秘密鍵をリモート先に引き継げるようにする。 https://timesaving.hatenablog.com/entry/2021/08/15/150000
配下でgit initを行う
- /etc/mysql/
- /etc/nginx/
MySQL
スロークエリログの設定
/etc/mysql/conf.d/mysql.cnf
に、以下の設定を追記すること。
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow_query.log
long_query_time = 0
max_connections=10000
# バイナリログ無効化
disable-log-bin=1
innodb_buffer_pool_size=1G
# ディスクキャッシュをストレージに同期することを無効
innodb_flush_method=O_DIRECT
sync_binlog=1000
innodb_flush_log_at_trx_commit=2
innodb_log_buffer_size = 128M
innodb_log_file_size = 512M
# サービス名を確認する
sudo systemctl list-units --type=service
UNIT LOAD ACTIVE SUB DESCRIPTION >
accounts-daemon.service loaded active running Accounts Service >
acpid.service loaded active running ACPI event daemon
mariadb.service loaded active running MariaDB 10.3.34 database server # ここはDB名による
# サービスをリスタートする
sudo systemctl restart mysql # ここはDB名による
設定が反映されてることが気になる場合は、
root@ip-xxx-xx-xx-xxx:~# mysql
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 71
Server version: 8.0.34-0ubuntu0.22.04.1 (Ubuntu)
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
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 variables like 'long%';
+-----------------+----------+
| Variable_name | Value |
+-----------------+----------+
| long_query_time | 0.000000 |
+-----------------+----------+
1 row in set (0.00 sec)
Nginx
alpインストール
wget https://github.com/tkuchiki/alp/releases/download/v1.0.21/alp_linux_amd64.zip
unzip alp_linux_amd64.zip
mv alp /usr/local/bin/
構造化ログを吐き出すようにする
tkuchiki/alp: Access Log Profiler を使って nginx のアクセスログを解析するために、ログフォーマットを json に変更します。
以下の内容で /etc/nginx/conf.d/log.conf
を作成しましょう。
log_format json escape=json '{"time":"$time_local",'
'"host":"$remote_addr",'
'"forwardedfor":"$http_x_forwarded_for",'
'"req":"$request",'
'"status":"$status",'
'"method":"$request_method",'
'"uri":"$request_uri",'
'"body_bytes":$body_bytes_sent,'
'"referer":"$http_referer",'
'"ua":"$http_user_agent",'
'"request_time":$request_time,'
'"cache":"$upstream_http_x_cache",'
'"runtime":"$upstream_http_x_runtime",'
'"response_time":"$upstream_response_time",'
'"vhost":"$host"}';
access_log /var/log/nginx/access.log json;
nginxの設定ファイルを変更したときは文法をチェックする
nginx -t -c /(nginxまでのパス)/nginx.conf
# sudo nginx -t -c /etc/nginx/nginx.conf
設定を書き換えた後、再起動
# 再起動 (コンフィグ書き換えたときは restart だと読み込まれないぽい)
sudo systemctl reload nginx
# 既存のログを消す
sudo rm /var/log/nginx/access.log
※ 既存の /etc/nginx/nginx.conf
と設定が被らないように「Logging Settings」をコメントアウトする必要がある
ベンチマーク実行 Tips
アプリケーションサーバを再起動する
// これでサービスリストを確認し。。
sudo systemctl list-units --type=service
// アプリケーションのサービスを再起動する (isu-go.service の場合)
// sudo systemctl restart isu-go.service
MySQL
スロークエリログを解析する
ログ中の実行時間の合計が長いクエリから順にひょうじします。 p.78
$ mysqldumpslow /var/log/mysql/slow_query.log
// Count: そのクエリが実行された回数
// Time: クエリの実行時間
// Locks: ロック関連の時間? (ロック競合してる場合は値が増えそう?)
// Rows: 取得されたレコード数の平均
Count: 1491 Time=1.03s (1531s) Lock=0.00s (0s) Rows=2.9 (4296), isuconp[isuconp]@localhost
SELECT * FROM `comments` WHERE `post_id` = N ORDER BY `created_at` DESC LIMIT N
// 実行時間でソート
mysqldumpslow -s t /var/log/mysql/slow_query.log
explainで実行計画を確認する
クエリに対して実行計画を確認する
インデックスを追加する
-- 単一インデックスの場合
ALTER TABLE `{table_name}` ADD INDEX idx__{table_name}__{column_name} (`{column_name}`);
-- 複合インデックスの場合
ALTER TABLE `{table_name}` ADD INDEX idx__{table_name}__{column1_name}_{column2_name} (`{column1_name}`, `{column2_name}`);
init.sh のようにベンチ初期化時に実行されるファイルがある場合
CREATE TABLE `isu_condition` (
`id` bigint AUTO_INCREMENT,
`jia_isu_uuid` CHAR(36) NOT NULL,
`timestamp` DATETIME NOT NULL,
`is_sitting` TINYINT(1) NOT NULL,
`condition` VARCHAR(255) NOT NULL,
`message` VARCHAR(255) NOT NULL,
`created_at` DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY(`id`),
+ INDEX `IDX__isu_condition__jia_isu_uuid__timestamp` (`jia_isu_uuid`, `timestamp`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
####外部キー制約を追加する。(存在チェックを行っている場合などに有効)
ALTER TABLE `対象のテーブル名`
ADD CONSTRAINT `fk_isu_condition_jia_isu_uuid`
FOREIGN KEY (`対象のカラム名`)
REFERENCES `制約をつける外部テーブル名` (`そのテーブルのカラム`);
####アプリケーションとDBが同じインスタンスに存在する場合にソケットファイルを通じて通信を行うことで高速化することができる。
mysql_config --socket
dsn := fmt.Sprintf("%v:%v@unix(/var/run/mysqld/mysqld.sock)/%v?parseTime=true&loc=Asia%%2FTokyo", mc.User, mc.Password, mc.DBName)
スロークエリログをクリーンアップする
rm /var/log/mysql/slow_query.log
sudo systemctl restart mysql
Nginx
静的コンテンツを nginx から配信する
静的コンテンツを golang から配信している場合は、以下の設定を追記すると効果的。
server {
listen 80;
client_max_body_size 10m;
root /home/isucon/private_isu/webapp/public/;
+ location ~ ^/(favicon\.iso|css|js|img) {
+ root /home/isucon/private_isu/webapp/public/;
+ expires 1d;
+ }
location / {
proxy_set_header Host $host;
proxy_pass http://localhost:8080;
}
}
alp で nginx ログを解析する
// --sort=sum のようにすることで、任意の列でソートできる (sum でソートすると良さそう)
cat /var/log/nginx/access.log | alp json -r
// -m "/posts/[0-9]+,/@,image/*" のようにすることで、正規表現でエンドポイントをグルーピングできる
// /api/isu/:id /api/isu/:id/graph /api/isu/:id/icon
cat /var/log/nginx/access.log | alp json -r --sort=sum -m "/api/isu/[^/]+$,/api/isu/[^/]+/graph,/api/isu/[^/]+/icon"
CPU 使用率を見る
// プロセスごとの CPU 使用率を見たいなら
top
// 時系列で変化を見たいなら
dstat -c -C 0,1,2,3,total
提出直前にすること
- nginxのログを無効化する
- slow-query-logを無効化する
Makefile
# -----------------------------------------
# Benchmark
# -----------------------------------------
.PHONY: benchmark
benchmark: api-restart mysql-restart nginx-restart
cd /home/isucon/bench && ./bench -all-addresses 127.0.0.11 -target 127.0.0.11:443 -tls -jia-service-url http://127.0.0.1:4999
# -----------------------------------------
# MySQL server
# -----------------------------------------
.PHONY: mysql-restart
mysql-restart: mysql-clean-logs
sudo systemctl restart mariadb
.PHONY: mysql-analytics
mysql-analytics:
mysqldumpslow /var/log/mysql/slow_query.log
.PHONY: mysql-clean-logs
mysql-clean-logs:
sudo rm /var/log/mysql/slow_query.log
# -----------------------------------------
# API server
# -----------------------------------------
.PHONY: api-restart
api-restart: api-compile
sudo systemctl restart isucondition.go
.PHONY: api-compile
api-compile:
cd /home/isucon/webapp/go && go build -o isucondition main.go
api-logs:
journalctl -u isucondition.go
# -----------------------------------------
# Nginx server
# -----------------------------------------
.PHONY: nginx-restart
nginx-restart: nginx-cleanup-logs
sudo systemctl restart nginx
.PHONY: nginx-cleanup-logs
nginx-cleanup-logs:
sudo rm /var/log/nginx/access.log
.PHONY: nginx-analytics
nginx-analytics:
cat /var/log/nginx/access.log | alp json -r --sort=sum -m "/api/isu/[^/]+$,/api/isu/[^/]+/graph,/api/isu/[^/]+/icon,/api/condition/[^/]+,/isu/[^/]+$,/isu/[^/]+/graph,/isu/[^/]+/icon,/isu/[^/]+/condition,/assets/[^/]"
# $はエスケープされるので $$ にする必要がある
-----------------------------------------
DB 分割
-----------------------------------------
セキュリティグループを編集して、アプリケーションインスタンスからDBインスタンスへの接続を許可する
pingでアプリケーションインスタンスから疎通確認を行う
ping 10.0.XX.XX # DBインスタンスIP
DBインスタンスにおいて、/etc/mysql/my.cnfに下記の設定を追加する
[mysqld]
bind-address = 0.0.0.0 # すべての IP アドレスからの接続を許可
他のインスタンスからアクセス可能なユーザーを作成する
CREATE USER 'isucon'@'%' IDENTIFIED BY 'isucon';
GRANT ALL PRIVILEGES ON *.* TO 'isucon'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
mysqlの再起動
sudo systemctl restart mysql
アプリケーションインスタンスからDBインスタンスのMysqlへアクセスする
mysql -h 3.113.3.39 -u isucon -p