CloudWatch Logs Insights を使って MySQL の performance_schema を分析する

Posted on

freee のプロダクトは RDBMS に MySQL を使っています。これまでもさまざまな方法でパフォーマンス改善を行ってきましたが、新たに CloudWatch Logs Insights を使って MySQL の performance_schema を分析する仕組みを作りました。

具体的には CloudWatch Dashboard を使って問題のありそうなクエリを見える化しました。次のスクリーンショットは一例ですが、このリストは CloudWatch Logs Insights にクエリを投げた結果です。

CloudWatch Dashboard

当初は DWH 用の Redshift に取り込んで Redash で分析するつもりでしたが、CloudWatch Logs Insights を使うことでシンプルに実装することができました。

Fargate と ECS の Scheduled Tasks を活用

performance_schema は時間の経過とともに変化していくので、定期的に取得して時系列に保存する必要があります。最初はエクスポートするスクリプトを Cron で実行するつもりでしたが、運用するサーバを増やしたくなかったので ECS の Scheduled Tasks を使うことにしました。エクスポートは数十秒で終わるので、Fargate を使うことでコストもミニマムに収めることができました。

また、エクスポートする Ruby スクリプトを Docker コンテナに閉じ込めることができたので、すべての依存関係をコード化することができました。手作業で構築・設定してしまうと暗黙知を生んでしまうので、こうしたバッチ処理も Pull Request ベースで進められるようになったのは大きなメリットです。

JSON Lines 形式で標準出力に書く

Fargate で動いている Ruby スクリプトは performance_schema と sys のテーブルを SELECT して、最終的に 1 レコードを 1 JSON で標準出力に書いています。

#!/usr/bin/env ruby

require 'date'
require 'json'
require 'mysql2'

export_date = DateTime.now.strftime('%Y-%m-%d %H:%M:%S')

table_names = [
  'performance_schema.events_statements_summary_by_digest',
  'sys.statement_analysis',
  'sys.innodb_lock_waits',
  'sys.statements_with_full_table_scans',
  'sys.schema_unused_indexes',
  'sys.schema_index_statistics',
  'sys.statements_with_errors_or_warnings',
]

client = Mysql2::Client.new(
  :host     => ENV['DB_HOST'],
  :username => ENV['DB_USER'],
  :password => ENV['DB_PASSWORD']
)

table_names.each do |table_name|
  query = 'SELECT * FROM ' + table_name
  json = {
    :export_date => export_date,
    :table_name  => table_name,
  }

  begin
    client.query(query).each do |row|
      json[:result] = row
      puts JSON.generate(json)
    end
  rescue => e
    json[:result] = nil
    json[:error_message] = e.message
    puts JSON.generate(json)
  end

  sleep 3
end

client.close

CloudWatch Logs Insights は JSON の構造を自動的に認識してサジェストしてくれます。また、1 レコード = 1 JSON の JSON Lines 形式にしたのは、将来的に Athena など他の分析ツールを使う可能性を考慮したからです。 CloudWatch Logs のデータは S3 にエクスポートできるので、柔軟に使い回せるログ形式にしておくと応用が効きます。

コンテナの標準出力は何もしなくても CloudWatch Logs に保存されるのでとても楽です。当初考えていた Redshift で分析する方法だと、S3 にファイルを作って COPY コマンドでロードさせるといった処理が必要になります。さらに、冪等性を考慮したりするとより複雑なコードになります。それと比べると標準出力に書くだけであとは AWS に任せられるのは画期的です。

まとめ

CloudWatch Logs Insights は Athena や Redshift を使うまでもないシンプルな分析用途には十分使えることがわかりました。他にもログ分析基盤として Elasticsearch を使っているのであれば、CloudWatch Logs Insights は有力な代替候補になりそうです。

S3 と比べると GB あたりの値段は CloudWatch Logs の方が圧倒的に高いので、分析したい用途に応じて使い分けることがポイントです。