当日やることメモ - 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