Timee Product Team Blog

タイミー開発者ブログ

成長サービスのDB負荷に向き合う、Platform Team と Stream-aligned Teamで取り組む性能改善

はじめに

こんにちは! タイミーでPlatform Engineerをしている @MoneyForest です。

自分が所属しているチームでは、週一で「観測会」を実施しています。 サービスの負荷状況が分かるダッシュボードを確認したところ、ある時期から Aurora MySQL の Reader CPU 使用率が大きく上昇していることに気づきました。

原因になりそうな処理は見えてきましたが、関連する機能はすでに利用されていたため、単純にリバートできる状況ではありませんでした。そのため、インスタンスを追加して一時的にしのぎつつ、根本的な改善を進める必要がありました。

この記事では Reader CPU 急増への対応を題材に、Platform Team が Datadog で事実を収集し、Stream-aligned Team(機能開発チーム)と協力して性能改善を進めた流れを紹介します。

1. Reader CPU が急増し、単純なリバートでは解決できなかった

ある時期から、Aurora MySQL の Reader CPU 使用率が通常時よりも上昇しました。

まずはサービスへの影響を避けるため、Reader インスタンスをスケールアップし、必要に応じて追加する暫定対応を行いました。ただし、これはあくまで暫定対応です。この状態が長期化するとコスト面で健全ではないため、並行して根本原因の特定に着手しました。

最終的にポイントだったのは、原因である機能を簡単に止められる状況ではなかったことです。その機能はすでに利用されており、利用増加に伴って、もともと非効率だった処理が顕在化した形でした。

そのため、機能として必要な振る舞いを保ちながら、処理をどう改善できるかを見極める必要がありました。

2. Datadog Notebook で事実をまとめ、温度感を揃えた

最初に行ったのは、Datadog Notebook に事象をまとめることでした。

CPU が高いこと、重そうなクエリがあること、暫定対応した時期、関係していそうな処理といった情報が Slack 上に散らばったままだと、認識が揃いません。

特に、「どれくらい危ない状況なのか」「暫定対応の結果どうなったのか」「恒久対応をどう進めるべきか」「どのチームに何を相談したいのか」を一箇所にまとめないと、適切な温度感が伝わりづらくなります。

そこで Datadog Notebook に、以下のような情報を集約しました。

観点 Notebook で整理した内容 添付したウィジェット・リンク
起きていること Reader CPU が通常時より大きく上昇し、いつ障害になってもおかしくない水準まで到達していた Reader CPU の推移が分かるグラフ
負荷の変化 特定クエリの実行頻度が大きく増加し、1回あたりの実行時間や走査行数も高い状態だった クエリ実行頻度、実行時間、走査行数のグラフ
暫定対応 サービス影響を避けるため、Reader インスタンスを追加して一時しのぎしていた 対応時刻やインスタンス追加・入れ替えの時系列
原因の仮説 ある機能の作成件数増加と、クエリ自体の構造的な重さが重なって Reader CPU に影響していそうだった 作成件数増加の時系列、DBM の Query Signature へのリンク
問題の分解 負荷に寄与しているクエリは複数あり、求人作成・更新時に走るものと、毎時の定期バッチで走るものに分けて確認した Query Signature ごとの DBM リンク、APM のトレースリンク
改善方針 一方はクエリ自体の書き換えが必要で、もう一方は既存の結果テーブルを参照する形に変えられる可能性があった 発行元の処理名、トレース、DBM / APM のリンク
相談したい判断事項 既存の結果テーブルを使うと対象者の範囲が変わる可能性があるため、機能仕様として許容できるかを Stream-aligned Team に確認したかった 判断に必要な調査メモと、根拠となる DBM / APM のリンク

このとき意識したのは、単にダッシュボードのようにグラフを列挙するのではなく、関係者が判断できる形に情報を並べることです。

CPU 使用率や重いクエリのメトリクスだけでは、「いま何が起きているのか」は分かっても、「どれくらい急ぐべきか」「誰に何を相談したいのか」「暫定対応で耐えられるのか」は伝わりません。

また、原因仮説は Platform Team 側で、Datadog から見えた処理名や実行タイミングを手がかりに、関連する変更履歴や実装を確認しながら立てていきました。その際はAI も活用し、どの機能・処理と関連していそうか当たりをつけました。

3. DBM / APM で重い Query Signature と呼び出し元を特定した

Reader CPU の上昇など、データベースのリソース使用状況の悪化は Metrics で把握できます。しかし、それだけでは何を直せばよいかは分かりません。

そこで次に、どのクエリが Reader に負荷をかけているのかを調べました。

mysql.queries.time と query_signature

ここで見たのが、Datadog DBM の mysql.queries.time メトリクスと、そのラベルである query_signature です。

mysql.queries.time は、正規化されたクエリごとの実行時間を表すメトリクスです。ざっくり言うと、1回あたりの実行時間 × 実行回数 に近い値として見ることができます。

query_signature は、SQL の具体的な値を取り除いて正規化したクエリの識別子です。条件に入る ID や日時だけが異なる SQL を、同じ種類のクエリとしてまとめて確認できます。

ただし、これは直接的に「クエリ単体の性能」だけを表すものではありません。1回あたりは速いクエリでも、実行回数が急増すれば mysql.queries.time は増えます。

逆に、1回あたりが遅いクエリでも、ほとんど実行されなければ全体負荷への寄与は小さく見えます。

一方で、今回のように「Reader CPU が上がっている」という事実に対して、「どの種類のクエリが寄与していそうか」を特定するには、非常に有用なメトリクスです。

調査は、次の流れで進めました。

  1. Metrics で Reader CPU の上昇タイミングを見る
  2. mysql.queries.time から相関関係のあるquery_signatureを見る
  3. Count / AVG Duration / Rows Scanned を見て、実行回数と1回あたりの重さを確認する
  4. DBMのUpstreamからAPMをたどり、そのクエリがどの処理から呼ばれているのかを確認する

この調査により、クエリの種類と、その呼び出し元の処理を特定できました。

具体的には、ある機能に関連する非同期処理から発行されるクエリが増加していました。対象データ量の増加に伴って、複数の条件を組み合わせた抽出処理が重くなり、Reader 負荷に大きく寄与している状態でした。

4. Datadog だけでは分からないことを Stream-aligned Team と確認した

どのクエリが重いか、いつ実行されているか、どの処理から呼ばれているかは分かりました。

しかし、Datadog だけでは以下は分かりません。

  • その処理は何の機能のために存在しているのか
  • 抽出対象のデータは、機能上どのような意味を持つのか
  • 他のデータやキャッシュされた結果が使えるのか
  • 機能利用がなぜ増えているのか
  • 最終手段として、機能制限や一時停止が取り得るのか

ここで、機能開発を担当する Stream-aligned Team のドメイン知識が必要になりました。

Platform Team 側では Datadog を見ながら負荷の原因を整理し、Stream-aligned Team 側では仕様や処理の中身を確認しました。Slack やハドル、Datadog Notebook で状況を共有しながら、「どの処理が負荷に寄与しているのか」「仕様を壊さず処理を変えられるのか」「もし改善しない場合に、停止などの措置は取り得るのか」を相談しました。

今回重要だったのは、技術的な事実だけでなく、「この状況をどれくらい危険と見ているか」を共有することでした。

Platform Team では、今回の CPU 負荷上昇に対して暫定対応として Reader インスタンスのスケールアップと追加を行いました。 この対応により CPU 使用率を一定以下に抑えることができたため、サービス影響を抑えることはできました。 しかし、依然として以下の問題を抱えていました。

  • CPU負荷上昇の原因となっているクエリ発行元の機能の利用者は増加傾向にあり、サービス影響が生じる可能性がある。
  • スケールアップ対応により、インスタンス使用量のコストが対応前より増加している。もともと想定していたDBにかかる費用を超過しているため、コストを抑えたい。

利用増は外部要因の影響もあり、こちらで直接コントロールしづらい状況でした。この背景を踏まえ、機能開発を担当していたチームに、数日で直す必要があることを伝えました。

一方で、機能を担当するチームから見ると、単に「このクエリが重い」と言われても、それがどれくらい急ぎなのか、すぐ直すべきなのか、仕様変更や一時停止まで検討すべきなのかは判断しづらいはずです。

このときの会話は、観点ごとに整理すると以下のようになります。

観点 Platform Team が確認したこと Stream-aligned Team と確認したこと
認識合わせ Notebook にまとめた経緯に認識齟齬がないか 認識齟齬がなく正しそうであること
背景・要因 負荷増加がどの処理と関連していそうか 機能利用の増加が関係ありそうなこと
改善方針 重い処理を改善できないか クエリの修正が可能そうであること
ワーストケース 機能制限や一時停止を最終手段として取り得るか 事業部との調整が必要そうであること

この会話によって、単なる技術調査ではなく、障害リスク・コスト・仕様影響・事業影響について議論できるようになりました。

最終的には、Stream-aligned Team が仕様上の判断をしたうえで、重い抽出処理を避ける方針やクエリ自体の改善を進めてくれました。

改善前は、複数の条件に該当する対象者を SQL 側で一度に抽出していました。その結果、同じ対象集合を使うサブクエリが複数回展開されたり、複雑な条件が組み合わさったりしていました。対象データ量や実行頻度が増えるにつれて、この構造が Reader に大きな負荷をかける要因になっていました。

改善後は、仕様の互換性を保ちながら、処理をいくつかの小さな取得に分け、アプリケーション側で結果を統合する形に変更しました。これにより、SQL 側で複雑な条件を一度に処理させる必要がなくなり、Reader にかかる負荷を抑えられるようになりました。

5. 改善後も Datadog で効果を確認した

クエリを修正すると、Datadog DBM 上の Query Signature が変わります。

そのため、改善前後を見るときに、単純に同じ Query Signature の before / after だけを見ても判断できません。今回も、修正後に対象クエリが分割され、新しい Query Signature が増えました。

そこで、以下の観点で改善効果を確認しました。

  • 変更前の重い Query Signature が減っているか
  • 変更後に増えた Query Signature の AVG Duration は許容範囲か
  • Total Duration は減ったか
  • 対象となる処理の実行時間は改善したか
  • Reader CPU 使用率に変化があったか

結果として、修正前に時間がかかっていたケースが、修正後は主要なクエリで大きく改善していることを確認できました。また、翌日に改めてメトリクスを確認すると、デプロイ以降で Reader CPU にも改善傾向が見られました。

一方で、Reader 全体の実行クエリ数も増えていたため、残る負荷はクエリ単体の問題ではなく、ワークロードの増加として切り分けました。ここまで判断できると、次はアプリケーション改善ではなく、キャパシティやコストの議論として扱えます。

6. まとめ

今回の対応で重要だったのは、重いクエリを見つけることだけではありませんでした。

本番サービスでは負荷の原因になっている機能は単純に止められないことがあります。だからこそ、Datadog DBM / APM / Metrics で負荷を分解し、Datadog Notebook に時系列・メトリクス・仮説・暫定対応・相談したい判断事項をまとめることで、関係者が同じ事実を見ながら会話できる状態を作りました。

そのうえで、Platform Team が整理した事実やリスク、温度感を共有し、受け取った Stream-aligned Team は機能仕様・実装方針・事業影響を踏まえて改善を進めてくれました。

性能課題は、重いクエリを見つければ終わるものではありません。どの負荷が危険で、どの判断が必要で、誰のドメイン知識が必要なのかを整理し、事実を整理して進めることが重要です。

タイミーではこのように、Platform Team と Stream-aligned Team が協力しながら、サービスの成長に伴って生まれる性能課題に向き合っています。