giftee Tech Blog

ギフティの開発を支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信しています。

スロークエリ攻略の糸口を探ろう! - お役立ち Tips たち -

タイトルサムネイル

こんにちは、エンジニアの toki (@tokai235) です。法人向け eギフトサービス giftee for Business の開発をしています。普段はバックエンドやインフラのお仕事が多いですが、フロントエンドも好きです。

この記事は、ギフティ Advent Calendar 2025 の 9日目の記事になります。

アプリケーションは運用していくとデータ量やトラフィックが増えて、パフォーマンスが劣化していくものです。パフォーマンス改善の切り口はたくさんありますが、その中でも特に効果が大きく、よく行われるのがデータベースのスロークエリの改善ではないかと思います。

実は最近スロークエリの改善をやりまして、そのときに得た知見などを紹介できればと思います。特に今回は具体的な改善には踏み込まず、改善すべきポイントの糸口を探ることにフォーカスしています。

また前提として、この記事では giftee for Business で利用している Amazon Aurora MySQL で話を進めますが、その他のインフラであったとしても思想は通じるものがあると思うので、そういった方もぜひ参考にしてもらえれば嬉しいです。

スロークエリの統計を見る

過去にスロークエリの改善をしようとしてこんな経験はないでしょうか?

  • スロークエリログを見たり、Slack 通知をしてみたりする
  • 結構な数のスロークエリが出ている...
  • でも定時バッチや分析用のクエリだったりして、それは改善しなくても問題ない
  • 結局スロークエリは発生しているものの、どこをどう改善すればいいのかわからない...

昔の自分も経験したことがありますが、今になって思うとこうなってしまうのは「見ている指標が少ないから」だと思っています。

スロークエリと言うとクエリの長さ(Query time)に目が行きがちですが、他にも重要な指標がいくつかあります。具体的には以下のようなものです。

  • クエリ実行数(Calls/sec)
  • スキャン行数(Rows examined)

なぜこれらの指標が重要なのでしょうか?

クエリ実行数が重要な理由

多くのアプリケーションでは、データベースとのコネクションは並列で複数持っていることが多いです。例えば Ruby on Rails の場合、Connection Pool はデフォルトで5つまで接続を増やし、プールします。

この場合、1つのコネクションが重いクエリで占有されていたとしても、残りのコネクションで処理しきることができればサービスとしては問題ありません。定時バッチなどはこれにあたることが多いでしょう。問題は、すべてのコネクションが重いクエリで詰まってしまうことにあります。

たとえクエリの処理時間があまり遅くなく、他に比べて目立たなかったとしても、クエリが1秒に100回や1000回などたくさんリクエストされる場合、処理待ちの大行列ができてしまい、パフォーマンス劣化が起きてしまいます。最悪の場合データベースがダウンしてしまうこともあるでしょう。つまり、負荷は「1つあたりのクエリの長さ × リクエストされるクエリの数」に比例し、これを見る必要があるということです。

スキャン行数が重要な理由

パフォーマンスというと速度に目が行きがちですが、リソース効率もとても重要です。そもそもデータベースの世界では、Query time が秒単位というのは「遅い」部類です。そのため、1秒程度で返ってくるクエリでも、裏では非常に多くのレコード行をスキャンしていることがあります。

スキャン行数が多ければ多いほど、CPU やメモリを多く消費します。そのため、レスポンスに問題はないが CPU が100%で張り付いてしまい、処理が継続できないということが起こります。

AWS で統計を見る方法

AWS であれば、CloudWatch Database Insights を使うと、これらの指標をまとめて見ることができます。ただしT系などインスタンスタイプによっては有効にできない場合があるのでご注意ください。ちなみにこの Database Insights は RDS / Aurora で提供されていた Performance Insights の後継とも言えるサービスで、概ね使い心地は同じと言っていいでしょう。

Database Insights では、「DB 負荷分析」の「上位の SQL」で SQL ごとに以下のような情報を確認できます。

  • wait によるロード(AAS): データベース上で同時にアクティブなセッション数(待機中のものを含む)
  • SQL ステートメント: 実行された SQL 文
  • Calls/sec: 1秒間で実行されたクエリ数
  • Avg latency(ms)/call: クエリの平均処理時間
  • Rows sent/sec: 1秒間のクエリ実行で結果として返した行数
  • Rows examined/call: 1回のクエリ実行でスキャンした行数
  • Created tmp tables/call: 1回のクエリ実行で作られた一時テーブルの数

AWS CloudWatch Database Insights Dashboard

CloudWatch Database Insights のダッシュボード画面

他にも色々なデータが見れるのですが、私がよく見ているのはこのあたりです。ただ、これだけを見せられても「これをどうするねん」となってしまうので、どういう使い方をしているか、いくつか例を交えてご紹介したいと思います。

例1: インパクトが大きいもの

単純に「wait によるロード(AAS)」でソートして大きい順に見ると、そこが伸びしろが大きいクエリであることが多いです。「待ち」が多く発生してるわけだからそりゃそうですね。またこの wait ロードの多さは Calls/sec と比例することが多いです。ここに入ってくるのは Query time がそれほど長くないものもあると思いますが、個人的には Avg latency(ms)/call が 100ms を超えているものは実行計画に改善の余地がありそうだな、と予想します。SQL ステートメントからもとの SQL 文がわかるので、EXPLAIN などを使って実行計画を見ていきましょう。

例2: リソース負荷を下げやすそうなもの

Rows examined に対して Rows sent が極端に小さいものは改善の余地ありです。これはつまり「数行のレコードを返すために数万行のレコードをスキャンしている」ような状態です。データベースのリソース負荷はスキャン行数によって大きく変わります。集計バッチのような返す行数自体がそもそも多いものは仕方ないこともありますが、返す行数が少ないのにスキャン行数が多い場合はスキャン行数を絞る余地が残っているので、where や join などの条件を見直してみましょう。

スロークエリの芽を見つける

ここまではパフォーマンス改善をやろうとしたときに、その糸口を探るためのあれこれを書いてきました。ただ、パフォーマンスが悪いことがわかったときって、結構問題が大きくなった後ってこと、ないですか?例えばデータベースの CPU やメモリは監視しているプロダクトは多いと思いますが、これらに影響が出る頃にはパフォーマンスの問題はかなり大きくなった後で、急いでやらないとまずい、といった状況になったりします。

実は今回私がスロークエリ改善をやることになったのもまさにこれがきっかけで、パフォーマンスが悪化した結果、ビジネス上影響が出て初めて対処するという形になりました。しかしそうなる前に徴候を知ることはできないのでしょうか?

RDS の SlowQueries メトリクスを監視する

AWS であれば、RDS のクラスタやインスタンスから SlowQueries(スロークエリの数)というメトリクスが取得できます。これを監視しておくと、スロークエリのパフォーマンス劣化がまだ小さい段階で見つけることができると思っています。メトリクスの形で取れていれば CloudWatch Alarm などで通知をすることもできますね。

RDS SlowQueries Metrics

RDS SlowQueries メトリクス

例えばこんな風に使えそうです。

  • リリースによってスロークエリが増えたかどうかを見る
  • スロークエリ数が特定のしきい値を超えた場合にアラートを発報する
  • 月ごとのスロークエリ数を定点観測し、スロークエリが増えたかどうかを見る

giftee for Business では比較的運用コストの少ないアラートの発報を設定することにしました。

ちなみに SlowQueries メトリクスに全然変化がないけど?という場合、スロークエリログのしきい値が適正でないかもしれません。スロークエリログに記録されるクエリの長さのしきい値は long_query_time によって決まりますが、MySQL のデフォルト値は10秒になっています。もしこれが長過ぎる場合、ほとんどのクエリがログに記録されない、といったことが起こってしまいます。そんなときは発行されている SQL のメトリクスなどを見ながら、適正な値に変えてしまいましょう。giftee for Business では今のアプリケーションの状況を考慮して2秒に設定していますが、1秒や0.5秒あたりが個人的によく見る設定かなと思います。

まとめ

この記事では、スロークエリの改善のためのヒントや Tips を紹介しました。特に今回は

  • パフォーマンス改善をやりたいが、その糸口を見つけたい
  • 問題が大きくなる前にパフォーマンス劣化に気づきたい

という観点でお話ししました。

スロークエリの改善は地道な作業ですが、小さな改善で大きなインパクトを生み出したりすることもあり、個人的にはとてもエキサイティングな領域だと思っています。

参考: 過去にやったパフォーマンス改善
tech.giftee.co.jp

ギフティでは、パフォーマンス改善に取り組むエンジニアも、これからやってみたい!という方も大募集しています。興味がある方はぜひカジュアル面談などでお話しましょう!

giftee.co.jp

herp.careers