fluent-plugin-mysql-status で MySQL パフォーマンス情報を常時収集する

<div class="alert alert-info text-center">この記事は<a href="http://qiita.com/advent-calendar/2016/gumi">「gumi Inc. Advent Calendar 2016」</a>25日目の記事です.<br />
この記事の内容は個人の見解であり, 組織の公式見解ではありません.</div>

# 動機

稼働中の MySQL サーバのパフォーマンス情報を収集する方法は幾つかあり, それぞれ用途に応じて使い分けが必要である. gumi では負荷試験時や新サービス公開後の一定期間だけ [JetProfiler](https://www.jetprofiler.com) を用いて詳細なパフォーマンス情報を収集し, パフォーマンス改善に役立てている.

しかし, JetProfiler も万能ではなく, 次の点で使い難い.

- 更新が滞っている
 - Performance schema に未対応
 - [sys schema](https://github.com/mysql/mysql-sys) に未対応
- データの収集/変換/表示がクライアントアプリケーション上で行われる
 - 同時に複数のホストから情報収集を行えない
 - アプリケーションを起動しないと情報収集できない [^1] [^2]
 - データ共有が煩わしい [^3] [^4]

類似のツールとして [MySQL Workbench](http://dev.mysql.com/downloads/workbench/) が存在するが, JetProfiler よりデータの可視性に劣り, クライアントアプリケーションである弱点も JetProfiler と同様である.

そこで, これらの問題を次の方法で解決する.

- パフォーマンス情報の収集を行う fluentd のプラグインを作成する
- パフォーマンス情報の保存先は, S3 や BigQuery とする
- データの可視化や共有は, QuickSight や Data Studio に頼る

# 本記事が扱う範囲

パフォーマンス情報の収集を行う [fluent-plugin-mysql-status](https://github.com/gumi/fluent-plugin-mysql-status) の説明に焦点をあてる.
次の内容は, 別の記事で取り扱う予定がある.

- どのようなパフォーマンス情報を収集するべきか [^5]
- fluentd から保存先への経路をどうするきべか
- 収集した情報を元に, どのような可視化を行うべきか

# 本題

[fluent-plugin-mysql-status](https://github.com/gumi/fluent-plugin-mysql-status) という定期的に MySQL サーバに任意のクエリを発行するプラグインを作成した.

既に [fluent-plugin-mysql-query](https://github.com/y-ken/fluent-plugin-mysql-query) という同じ役割のプラグインが存在しているため, 当初は, そちらを使用する予定だったが, 次の理由から車輪の再発明に踏み切った.

- MySQL サーバへの接続数を減らしたい [^6]
- 複雑なクエリはファイルを分けたい
- よく使用するクエリは省略したい

### MySQL サーバへの接続数を減らしたい

fluent-plugin-mysql-status は, MySQL サーバに対して接続数に起因する負荷をかけないよう配慮している. source ごとに MySQL への接続を一つ保持するが, 次の例のように, 一つの source に複数のクエリを指定できるため, 発行するクエリ数が増加しても接続数は増加しない. 

```
<source>
  type mysql_status
  tag mysql

  <query>
    type processlist
    tag processlist
  </query>

  <query>
    type status
    tag status
  </query>
</source>
```

ただし, クエリを順番に実行するため, 遅いクエリを記述すると, 同一 source 内の他クエリの実行が遅延する. その場合, 該当クエリの source を新たな source へ隔離することで対処できる.

### 複雑なクエリはファイルを分けたい

fluent-plugin-mysql-status は, クエリを記述する方法が三種類ある.

|パラメータ名  |説明|
|-------------|----|
|path         |クエリが記述されたファイルのパスを指定する.|
|string       |クエリを直接記述できる.|
|type         |プラグインに組み込みのクエリを指定する. 後述.|

string で任意のクエリを記述できるが, 次のように sys schema で見覚えのあるクエリを記述すると設定ファイルの可読性が低下ちる.

```
<source>
  <query>
    string SELECT DIGEST_TEXT AS query, SCHEMA_NAME as db, COUNT_STAR AS exec_count, SUM_ERRORS AS errors, IFNULL(SUM_ERRORS / NULLIF(COUNT_STAR, 0), 0) * 100 as error_pct, SUM_WARNINGS AS warnings, IFNULL(SUM_WARNINGS / NULLIF(COUNT_STAR, 0), 0) * 100 as warning_pct, FIRST_SEEN as first_seen, LAST_SEEN as last_seen, DIGEST AS digest FROM performance_schema.events_statements_summary_by_digest WHERE SUM_ERRORS > 0 OR SUM_WARNINGS > 0
    tag errors_or_warnings
  </query>
</source>
```

そこで次のように path を使用すると, 設定ファイルの可読性が向上する.

```
<source>
  <query>
    path /path/to/x_statements_with_errors_or_warnings.sql
    tag errors_or_warnings
  </query>
</source>
```

### よく使用するクエリは省略したい

JetProfiler が定期的に発行するクエリを type で指定可能にした.

|type |発行されるクエリ|
|-----------|---------|
|processlist|SHOW FULL PROCESSLIST|
|status     |SHOW /*!50002 GLOBAL */ STATUS|
|open_tables|SHOW OPEN TABLES|

今後, ベストプラクティスを私自身が学びながら, type の種別を増やす検討をしている.

# まとめ

ハッピーホリデイ！

# 参考資料

- https://www.datadoghq.com/blog/monitoring-mysql-performance-metrics/
- https://www.datadoghq.com/blog/collecting-mysql-statistics-and-metrics/
- https://www.datadoghq.com/blog/mysql-monitoring-with-datadog/
- https://github.com/percona/percona-toolkit
- https://github.com/mysql/mysql-sys

# 注釈

[^1]: 個人所有の端末の場合, アプリケーションを常時起動し続けられないことがる.
[^2]: 専用端末を用意するとコストが嵩む.
[^3]: 方法1: HTML などのレポートファイルを配布する.
[^4]: 方法2: データファイルを配布し, 同アプリケーションをインストールしたクライアントで再生する.
[^5]: 本当は, この記事で扱う予定だったがタイムオーバーになった. すまぬ.
[^6]: fluent-plugin-mysql-query は, 記述したクエリの数と MySQL サーバへの接続数が同数となる.