クエリログを使ったAurora MySQLの負荷テスト

最近はZX-25Rが気になっている菅原です。4気筒250ccといえば、以前バリオス2に乗っていたんですが、あれもよく回るよいバイクでした。足つきの良さが懐かしいです。

この記事では、クエリログを使ったAurora MySQLの負荷テストの話を書きます。

MySQLの負荷テスト

サービスに使われているデータベースは、Webサーバと比べて自動的なスケールアップ・スケールアウトが簡単ではないためキャパシティプランニングは非常に重要です。サービスへのアクセス増による負荷増大の結果、急激に性能が低下するためなるべく事前にキャパシティを把握しておきたいところです。

クックパッドではサービスのデータベースとして主にAurora MySQLを利用しているのですが、キャパシティを把握するための負荷テストには以前から苦労してきました。

1. シナリオを書くのが大変

サービスで使われているデータベースの負荷テストのシナリオを人間が書こうとすると、あるシナリオでは極端に性能がよくなり、またあるシナリオでは極端に性能が悪くなるということがあり、実際のサービスを模してシナリオを書くのが非常に困難で、意味のある負荷テストをすることが難しいことが多くありました。

2. 大きなサイズのシナリオを実行する負荷テストツールがない

データベースの負荷テストツールは大きく2種類あり、一つはTCP-xのような決められたモデルを実行するツールで、もう一つは任意のシナリオを実行するツールです。TCP-xを実行するツールはクックパッドのサービスの性質とは異なるため、任意のシナリオを実行するツールを用いて負荷テストを行っていたのですが、MySQLで任意のクエリやシナリオを実行するツールはあまり多くなく、私の知る限りは以下の通りでした。

クエリログを使ったMySQLの負荷テスト

今までは上記のツールを使って負荷テストを行っていました(特にJdbcRunnerはとても便利でした!)。

しかし、既存のツールにはいくつか不満がありました。

1. 基本的にシナリオを人間が書く必要がある&大きなシナリオを読み込むのにメモリが必要

mysqlslapを除き任意のシナリオを実行することができるのですが、当然、人間がDSLやJavaScriptでシナリオを書く必要があり、前述の通り、実際のサービスに沿ったシナリオを書くことが困難でした。また、シナリオはメモリに読み込まれるため、大量の異なるデータをテストデータにしようとすると、クライアント側にデータを読み込むための大量のメモリが必要になりました。

2. 結果のデータの解析が難しい

負荷テストの実行結果について、平均応答時間だけではなく、中央値やヒストグラムなどが欲しいのですが、あまり多くの情報は出力されなく、出力されるデータは単純なテキストであったため、データをパースして結果を集計するにもやや手間がかかりました。

Webサーバの場合、アクセスログをテストデータとして負荷テストを行うことが多いと思います。MySQLに関しても同じようなことができないかと以前から考えていたのですが

  1. データが大きく(GB単位)、メモリに乗せるのが難しい
  2. データの羅列をDSLやJavaScriptに変換するのに手間がかかる

という問題がありました。

それらの問題を解決するためqrnという負荷テストツールを新たに作成しました。

qrn

データベース負荷テストツールqrnの大きな特徴は次の通りです。

  1. JSON Linesをテストデータとする
  2. テストデータは逐次ディスクから読み取り、全体をメモリにはロードしない
  3. テスト結果をJSONで細かく出力する

たとえば、以下のデータを

{"query":"select 1"}
{"query":"select 2"}
{"query":"select 3"}

4並列・5 qps/userで10秒間実行する場合、コマンドと出力結果は次のようになります。

$ qrn -data data.jsonl -dsn root:@/ -nagents 4 -rate 5 -time 10
00:07 | 4 agents / run 184 queries (20 qps)
{
  "DSN": "root:@/",
  "Files": [
    "data.jsonl"
  ],
  "Started": "2020-05-13T11:18:14.224848+09:00",
  "Finished": "2020-05-13T11:18:24.559912+09:00",
  "Elapsed": 10,
  "Queries": 189,
  "NAgents": 4,
  "Rate": 5,
  "QPS": 18.287694303306097,
  "MaxQPS": 21,
  "MinQPS": 18,
  "MedianQPS": 19,
  "ExpectedQPS": 20,
  "LoopCount": 15894,
  "Response": {
    "Time": {
      "Cumulative": "78.389862ms",
      "HMean": "392.47µs",
      "Avg": "414.761µs",
      "P50": "418.565µs",
      "P75": "462.099µs",
      "P95": "532.099µs",
      "P99": "735.68µs",
      "P999": "760.585µs",
      "Long5p": "632.823µs",
      "Short5p": "218.38µs",
      "Max": "760.585µs",
      "Min": "182.384µs",
      "Range": "578.201µs",
      "StdDev": "90.961µs"
    },
    "Rate": {
      "Second": 2411.0260584461803
    },
    "Samples": 189,
    "Count": 189,
    "Histogram": [
      {
        "57µs - 115µs": 1
      },
      {
        "115µs - 173µs": 1
      },
      {
        "173µs - 231µs": 4
      },
      {
        "231µs - 289µs": 14
      },
      {
        "289µs - 346µs": 12
      },
      {
        "346µs - 404µs": 48
      },
      {
        "404µs - 462µs": 63
      },
      {
        "462µs - 520µs": 34
      },
      {
        "520µs - 760µs": 12
      }
    ]
  },
  "Token": "a579889e-97f9-4fd1-8b33-93ab2c78e6ad"
}

クエリログのテストデータ化

MySQLのクエリログ(general log)をqrnのテストデータとして使用する場合、general logをJSON Linesに変換する必要があります。そのためのツールgenlogも新たに作成しました。

以下のようなgeneral logをJSON Linesに変換できます。

2020-05-27T05:03:27.500301Z   11 Query   SET @@sql_log_bin=off
2020-05-27T05:03:27.543379Z   11 Query  select @@session.tx_read_only
2020-05-27T05:03:27.683485Z   11 Query  COMMIT
$ genlog general.log # or `cat general.log | genlog`
{"Time":"2020-05-27T05:03:27.500301Z","Id":"11","Command":"Query","Argument":"SET @@sql_log_bin=off"}
{"Time":"2020-05-27T05:03:27.543379Z","Id":"11","Command":"Query","Argument":"select @@session.tx_read_only"}
{"Time":"2020-05-27T05:03:27.683485Z","Id":"11","Command":"Query","Argument":"COMMIT"}

また、クエリログからSELECT文のみを抽出したい場合は、jqを使ってフィルタリングします。

$ jq -rc 'select(.Command == "Query") | select(.Argument | test("select" ; "i"))' general.log.jsonl > mysql-general-select.jsonl

general logの出力については負荷増大の懸念から、今まで避けているところがあったのですが、実際に出力してみるとそれほどCPU使用率は上がらなかったため、現在では必要であれば出力するようにしています。

ただしAurora MySQLでgeneral logをCloudWatch Logsにエクスポートした場合は、それなりにCPU使用率が上がるため、Aurora MySQLのgeneral logを使用する場合にはエクスポートせずに、DBインスタンスの各ログをダウンロードするようにしました(ダウンロード用のツールも作成しました)。

テスト結果の集計

テスト結果は前述のようにJSONで出力しているのですが、どのクエリがどの程度応答時間を占めているのかを分析したい場合、負荷テストを実施しているテスト用データベースのlong_query_timeを0にして、すべてのクエリのスロークエリログを出力し、それをpt-query-digestで分析するということを行っています。

ウェイトを占めるクエリがわかるため、実際にサービスでスロークエリログが出力する前に重いクエリを改善するというようなことに役立ちます。

まとめ

データベースの負荷テストツールを自作することにより、クエリログをテストデータとした負荷テストが行えるようになり、Webサーバのような感覚で簡単にデータベースの負荷テストを行えるようになりました。これにより、事前のデータベースのキャパシティ把握が容易になったと考えています。

今はまだ手動で負荷テストを実施しているので、今後は負荷テストの自動化を目指したいところです。

iOSDC Japan 2020 に社員2名が登壇します

こんにちは!とくなり餃子大好き( id:tokunarigyozadaisuki )です。
すっかり秋の気配がしてきましたね。餃子を焼きやすい気候になって嬉しい!

さて、iOSと周辺技術を題材としたカンファレンス、iOSDC Japan 2020 が今年は9月19日(土)〜9月21日(月・祝)にオンラインにて開催されますね!

トークのご紹介

クックパッドは、ゴールドスポンサーをさせていただいております。 9月21日(月・祝)12:30〜 Track A では、スポンサーセッションとして、@yujif_@n_atmark が登壇します。

これまで公開したことのない、新しい取り組みに関するトークです。お楽しみに!

「クックパッドが、革新的な方法でまったく新しい買い物体験を皆様にお届けします」

クックパッドは「毎日の料理を楽しみにする」をミッションに、約20年前から料理のアイデアや工夫が流通する仕組みづくりに取り組んできました。現在も日本から世界中に広がる「プラットフォームをつくる」挑戦を続けています。

一方で、共働きの増加や核家族化、外食やデリバリーの普及など、毎日の料理を囲む環境は目まぐるしく変わってきています。

クックパッドはこれからの100年に向けて「レシピ」のその先にも取り組んでいます。 次の100年のスタンダードになる「食」の流通をつくるため、2018年に生鮮食品ECプラットフォーム「クックパッドマート」を開始しました。

「クックパッドマート」では今までにない仕組みで、毎日の献立づくり、買い物、調理すべてを変えることに挑んでおり、「新鮮で美味しい食材」を当たり前に安く買えるように、ゼロから食品流通プラットフォームの構築を進めています。

本セッションでは、クックパッドの新しい挑戦である「クックパッドマート」における開発や、既存の「レシピ」を組み合わせたまったく新しい買い物体験の実現、それらを支えるiOSアプリのアーキテクチャや SwiftUI の活用についてお話しします。

おわりに

カンファレンスには、他にも多くの社員が参加する予定です。スポンサーセッションなどに関してご質問やご感想等ございましたら、お気軽にお声がけください! 

クックパッドでは、iOSのサービス開発に一緒に取り組んでくれる仲間を募集しています。トークを見て少しでも興味を持っていただいた方にはこちらをご参照いただけましたら幸いです。

募集要項:iOSエンジニア

info.cookpad.com

Cookpad Online Summer Internship 2020 5 Day Engineer コースを開催しました

ユーザー・決済基盤部の三吉です。今年になってエンジニアの立場から新卒採用を担当しています。

5月の記事で告知したサマーインターンシップのうち、5 Day Engineer コースを 8/24〜28、9/7〜11 の2日程で開催しました。この記事ではその内容を紹介します。

3 Day Product Design コースについては、以下の記事をご覧ください。

5 Day Engineer コースは、前半2日間が講義形式、後半3日間が実践形式でした。 前半はプラットフォーム別の技術講義とサービス開発講義の2本立てです。 後半は PBL (Project-Based Learning) として、サービス開発の実践に取り組みます。

今年はオンラインでの実施となりましたが、例年同様たくさんの学生に参加いただきました。 以下、各パートの詳細です。

技術講義

初日のオリエンが終わった直後から2日目の午前までが技術講義でした。 申込時に選択した Web、Android、iOS のプラットフォーム別にクックパッドの開発手法について学びます。 例年はサーバーサイドの経験しかない学生にもモバイルの講義を受けてもらっていましたが、今年は募集時からプラットフォーム別だったので、例年より一歩踏み込んだ内容になっています。

Web

クックパッドで実際に行われている開発フローをベースに、AWS 上でのデプロイパイプラインの構築や Ruby on Rails アプリケーションの実装・改善を体験してもらいました。

Android

レシピの閲覧・投稿ができるアプリ MiniCookpad の開発を通して、Android 版クックパッドアプリで採用されているアーキテクチャやテスト手法を体験してもらいました。

iOS

Android と同じく、MiniCookpad の開発を通して iOS 版クックパッドアプリで採用されているアーキテクチャやテスト手法を体験してもらいました。内容の大筋は Android・iOS 間で事前にすり合わせています。

講義資料は リポジトリの Documents 以下 にあります。

サービス開発講義

2日目午後はサービス開発講義です。 半日という短い時間でしたが、クックパッドのサービス開発手法について講義したのち、あるユーザーの課題を解決するアプリケーションの考案やプロトタイプの作成まで行いました。 技術講義とは打って変わってコードを一行も書かない講義でしたが、参加者から「クックパッドのサービス開発手法を知ることができた」と好評でした。

PBL

後半、3日目からはサービス開発を実践する PBL です。 「一人暮らしをしている人の料理が楽しみになるアプリケーション」というテーマをもとに各自でアプリケーションを開発し、最終日に成果発表します。 PBL 中は社員がメンターとしてつき、サービスの相談に乗ったり開発をサポートしたりしました。

オンライン開催にあたって

スプリングインターンシップもオンラインでの開催でしたが、複数日にまたがって実施するのはこのサマーインターンシップが初めてでした。 ここでは、オンライン開催にあたっての取り組みを紹介します。

まず、オンライン開催にあたり、インターンシップ全体を通して利用したツールは次のとおりです。

  • Zoom
    • インターンシップ開催中は常に Zoom ミーティングに参加してもらうようにしました。チームに分かれる場合や、PBL の作業時間、懇親会等ではブレイクアウトルームを活用し、こちらからの一方通行のコミュニケーションにならないよう意識しました。
  • Slack
    • Zoom のチャット機能は使わず、文字ベースのコミュニケーションは Slack に統一しました。講義中など声を出しにくい状況でも、随時 Slack で質問を受け付けました。
  • Kibela *1
    • 連絡事項の共有や日報の提出に利用しました。Slack がフロー情報を扱うのに対し、Kibela ではストック情報を扱います。

オンライン開催による難点として、学生がオフィスの様子を知ることができないという点があります(当然ですが)。 これについて、オンラインでのオフィスツアーを実施しました。 社員がオフィスからスマートフォンで Zoom ミーティングに参加し、都度解説しながらオフィスを一周するというものです。

一方で、オンライン開催ならではの出来事もありました。 PBL 期間中、ある学生は開発中のアプリを友だちに使ってもらってユーザーインタビューしていました。 また、スプリングインターンに続き、海外から参加する学生もいました。 これらはオンラインで物理的制約がないからこそ可能なことです。

おまけ: ノベルティ

f:id:sankichi92:20200915115625j:plain
Tシャツ、エコバッグ、ステッカー、クックパッドロゴウォーター
f:id:sankichi92:20200915115728j:plain
自作キーボードキット Cookpad Pad 2(組み立て済み、裏側)

写真のノベルティセット(Tシャツ、エコバッグ、ステッカー、自作キーボードキット)を休憩時用の軽食と合わせて事前に送付しました。 また、この他にサマーインターンシップのロゴが入った Zoom のバーチャル背景用の画像も配布しています。

自作キーボードキット Cookpad Pad 2 について詳しくは以下の記事をご覧ください。


以上が、Cookpad Online Summer Internship 2020、5 Day Engineer コースの開催報告です。 ご参加いただいた皆さま、本当にありがとうございました!

今年のサマーインターンシップは終わってしまいましたが、クックパッドでは就業型インターンシップを通年で募集しています。 興味のある方はぜひご応募ください!

*1:これまでのサマーインターンシップでは Groupad という内製の情報共有ツールを利用していたのですが、例年と違って学生各自の PC から参加してもらう形をとった都合上使用できませんでした。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/