DB 高負荷時に数秒でサクッと原因を特定する (aws RDS)

# 概要

なんかよくわからないけど DB の負荷が高くなったどー
という監視アラートが出たとき、あなたはどうしますか？

そういうときに何が原因で負荷が高くなっているかサクッと追いたいですね。
CPU の状態や、スワップの状態、コネクション数から調べることもできますが、これだけでは何が原因で重くなったのかはっきりしないことは多いです。

# 対象読者

- aws が何なのか知っている人、運用している人 （aws は食べ物ではない)
- RDS で DB 管理していること
- スロークエリログを cloudwatch もしくは DB に保存できる環境であること

# 準備

## DB 側

数秒でサクッと原因を特定できるようにするためには、事前に準備が必要です

まずは RDS のパラメータグループで以下の設定を変更してください (デフォルトのパラメータグループでは変更できないようです)

- slow_query_log: 1
- long_query_time: 0.1 ~ (単位 秒)
- log_output: FILE (TABLE でもよいですが、その場合の方法は今回は割愛)

次に、RDS 自体の設定で [Modify] を設定し、[Slow query log] にチェックをいれます

そうすることで RDS の Configuration に Slow query という cloudwatch へのリンクができます。

<img width="659" alt="スクリーンショット 2019-12-05 9.06.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218682/8a727829-f087-7e38-f207-7788c1ce3bf3.png">

リンク先を開くと、キャプチャの ① が log-group-name で ② が log-stream-name です。メモしておきましょう。

<img width="1244" alt="スクリーンショット 2019-12-05 9.08.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218682/62399329-d867-3849-c88d-0b316580dad8.png">


## 集計側 (local など)

### セットアップ

mac の場合 homebrew 導入済みの前提で記載しますと、以下のように [percona-toolkit](https://www.percona.com/doc/percona-toolkit/LATEST/installation.html) を導入します

```
$ brew install percona-toolkit
```

jq コマンドも導入しておきます

```
$ brew install jq
```

mac の場合は、以下のように [aws-cli](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2.html) を導入します

```
$ brew install awscli
$ aws configure // aws の設定を行う
```

### スクリプト配置

ということで、 `slow-query-summary.sh` というスクリプトで中身を以下のようにします
pt-query-digest のオプションを指定すれば、explain も出したり、余計なクエリをフィルタしたりできますが、ここでは割愛します

`LOG_GROUP` には先程メモした `log-group-name` を、 `LOG_STREAM` には先程メモした `log-stream-name` をいれます

mac - FreeBSD系

```bash
#!/bin/bash -u

LOG_GROUP=hogehoge
LOG_STREAM=fugafuga

SCRIPT_NAME=`basename $0`
if [ $# -ne 2 ]; then
  echo "指定された引数は$#個です。" 1>&2
  echo "実行するには2個の引数が必要です。" 1>&2
  echo "Usage: $SCRIPT_NAME "\""2019-12-01 12:00:00"\"" "\""2019-12-01 13:00:00"\" 1>&2
  exit 1
fi

start="$1"
end="$2"

echo start datetime iso-8601..
echo $(TZ=JST-9 date -jf "%Y-%m-%d %H:%M:%S" "$start" +%s);
echo end datetime iso-8601..
echo $(TZ=JST-9 date -jf "%Y-%m-%d %H:%M:%S" "$end" +%s);

starttime=$(expr `TZ=JST-9 date -jf "%Y-%m-%d %H:%M:%S" "$start" +%s` \* 1000)
endtime=$(expr `TZ=JST-9 date -jf "%Y-%m-%d %H:%M:%S" "$end" +%s` \* 1000)

aws --profile prod logs get-log-events \
  --log-group-name ${LOG_GROUP} \
  --log-stream-name ${LOG_STREAM} \
  --start-time ${starttime} \
  --end-time ${endtime} \
  --query 'events[].message' \
  |jq -r '.[]' \
  | pt-query-digest --limit 10000
```

gnu 系

```bash
#!/bin/bash -u

LOG_GROUP=hogehoge
LOG_STREAM=fugafuga

SCRIPT_NAME=`basename $0`
if [ $# -ne 2 ]; then
  echo "指定された引数は$#個です。" 1>&2
  echo "実行するには2個の引数が必要です。" 1>&2
  echo "Usage: $SCRIPT_NAME "\""2019-12-01 12:00:00"\"" "\""2019-12-01 13:00:00"\" 1>&2
  exit 1
fi

start="$1"
end="$2"

echo start datetime iso-8601..
echo $(TZ=JST-9 date --iso-8601="seconds" --date "$start");
echo end datetime iso-8601..
echo $(TZ=JST-9 date --iso-8601="seconds" --date "$end");

starttime=$(expr `TZ=JST-9 date --date "$start" +%s` \* 1000)
endtime=$(expr `TZ=JST-9 date --date "$end" +%s` \* 1000)

aws --profile prod logs get-log-events \
  --log-group-name ${LOG_GROUP} \
  --log-stream-name ${LOG_STREAM} \
  --start-time ${starttime} \
  --end-time ${endtime} \
  --query 'events[].message' \
  |jq -r '.[]' \
  | pt-query-digest --limit 10000
```

最後に実行権限をつけます

```
$ chmod +x ./slow-query-summary.sh
```

重くなったなというときに、以下のようにスクリプトを実行すれば、簡単にスロークエリログのサマリが表示されるようになります。

```
./slow-query-summary.sh  "2019-12-01 12:00:00" "2019-12-01 13:00:00"
```

※キャプチャはサマリの部分だけですが、実際のスロークエリログもでてきます

<img width="1182" alt="スクリーンショット 2019-12-05 9.20.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218682/dad6a627-d93d-71d8-db29-764245e8a58e.png">