AKARI Tech Blog

燈株式会社のエンジニア・開発メンバーによる技術ブログです

検索画面が遅い原因はSQL文じゃなかった

皆さんこんにちは、AI SaaS事業本部 Digital Billder事業部 Devグループの御厨です!

私たちが開発・提供している Digital Billder は、建設業界における「見積・発注・請求」といった一連の業務をクラウドで一元管理できる Web アプリケーションです。

www.lp.digitalbillder.com

日々の業務の中で扱うデータ量も多く、請求書の検索体験を向上させることは、プロダクトの使いやすさに直結します。

今回は、その検索体験を改善するために、検索画面のパフォーマンス改善を担当しました。

当初は、

  • SQLが重いのでは?
  • インデックスが足りないのでは?

といった、よくある仮説から調査を始めました。

しかし実際に改善を進める中で、一番効いたのは別のポイントでした。

それは、

クエリを軽くする前に 「そもそも発行しすぎていないか」を疑うこと

本記事では、改善率(%) と Aurora Performance Insights の変化 を交えながら、検索画面パフォーマンス改善で得られた実務的な学びをまとめます。

改善の背景

対象となった検索画面では、以下の課題がありました。

  • 初回表示・検索実行時に明確な待ちが発生する
  • 条件を変更するたびに体感で遅さを感じる
  • データ量増加に伴い、今後さらに悪化する懸念があった

実際にお客さんからも「検索が遅い」という指摘があり、UX的にも無視できない状態でした。

そのため、場当たり的な対処ではなく、計測を前提にしたパフォーマンス改善に取り組むことになりました。

今回やったこと

まず最初に SQL やインデックスを疑って Performance Insights とアプリのログを追いました。
その結果、ボトルネックは「クエリの発行回数が多い」ことに加えて、発行されているクエリ自体も重いことだと分かりました。

具体的には、一覧画面の検索で本来必要な情報以上に、一括操作で必要になる詳細情報まで同時に取得しており、結果として「重いクエリが短時間に何度も走る」状態になっていました。

そこで改善として、取得するデータを用途で分離しました。

  • 検索(一覧表示)では、表示に必要な最低限の情報だけ取得する
  • 一括操作を実行したタイミングでのみ、必要な詳細情報を追加で取得する

まずは改善結果からお伝えします。

改善結果(改善率)

計測条件

  • 対象:請求書一覧の検索API(検索画面の「一覧取得」処理)
  • 件数:100〜200件程度(企業・条件により前後)
  • 計測環境:本番相当
  • 指標:表示完了まで(ユーザー体感に近い指標)

一覧画面表示(担当請求書)

企業 改善率
企業A 約74%改善
企業B 横ばい(条件によりブレ)
企業C 約42%改善
企業D 約38%改善
  • 4社中3社で明確に改善

一覧画面表示(全社請求書)

企業 改善率
企業A 約66%改善
企業B 約41%改善
企業C 約84%改善
企業D 約58%改善
  • 最大で80%超の改善が確認できました

一括操作(モーダル表示まで)

企業 担当請求書(改善率) 全社請求書(改善率)
企業A 約31%改善 約38%改善
企業B 横ばい 約11%改善
企業C 約31%改善 約39%改善
企業D 約23%改善 約36%改善

体感としても「待たされる感覚がほぼなくなった」状態になりました。

Performance Insights から見た改善効果

検索画面改善を含むリリースを 12月5日 に実施しました。

Aurora Performance Insights を見ると、リリース前後で以下の変化が確認できます。

改善前(〜12/4)

  • 平均アクティブセッション(AAS)が高止まり
  • 負荷の大半を CPU が占有
  • 検索操作が集中する時間帯でスパイクが頻発

改善後(12/5 以降)

  • AAS が明確に低下
  • CPU 由来の負荷が大きく減少
  • 負荷スパイクの頻度・高さともに改善

DB のスケール(vCPU)を上げていないにも関わらず負荷が下がっている点からも、クエリの発行数そのものが減った効果が確認できました。

今回一番効いた改善ポイント

クエリを軽くする前に「発行しすぎていないか」を疑う

今回の改善で最も効果が大きかったのは、SQLそのものの最適化ではありません。

実際に起きていたのは、以下のような状態でした。

  • 同一条件の検索が短時間に何度も発行されている
  • 一覧取得・件数取得・補助データ取得で似たような重いクエリが複数回実行されている
  • GraphQL resolver 経由で画面構造に引きずられたDBアクセスが発生している

これらを整理し、

  • 不要な再検索の抑制
  • 同一条件の検索結果の使い回し
  • 一覧画面で本当に必要な項目だけを取得

といった対応を行いました。

結果として、SQL自体をほとんど変えずに大幅な改善が得られました。

ここからは今回の改善を通して得た教訓です

以降は、今回の改善を通して「今後も意識したい」と感じたポイントをまとめます。

ヤマ勘で改善しない。まずは計測する

パフォーマンス改善で一番避けるべきなのは、

「たぶんここが重い」

という推測だけで手を入れることです。

今回、必ず確認していたのは以下です。

  • EXPLAIN / EXPLAIN ANALYZE
  • Seq Scan / Index Scan
  • JOIN 方法(Nested Loop / Hash Join など)

体感ではなく、実行計画と数値で判断する これを徹底しました。

GraphQL の resolver は特に注意が必要

GraphQL は便利な反面、裏で発行される SQL を意識しづらい仕組みです。

  • フィールド追加がSQL増加につながる
  • 素直に書くと N+1 が発生しやすい

実務では、

「この1リクエストで、何本SQLが出ているか」

を常に意識するようになりました。

実行計画は「環境ごとに別物」だと腹落ちした

今回の改善で改めて痛感したのは、実行計画は環境ごとに平気で変わるということです。

開発 / 検証 / 本番では、データ量・データの偏りが異なります。
その結果、同じSQLでも

  • Index Scan が効く / 効かずに Seq Scan になる
  • Nested Loop / Hash Join など JOIN 戦略が変わる
  • Sort が発生して一気にコストが跳ねる

といった形で、ボトルネックが入れ替わります。

だからこそ、改善は「ローカルで速くなった」で満足せず、
本番相当のデータ量で成立するかを前提に評価するのが重要です。

可能であれば、本番相当環境で EXPLAIN (ANALYZE, BUFFERS) を確認しながら、
“本番のクエリ” を改善する意識を持つようになりました。

まとめ

検索画面のパフォーマンス改善は、

  • SQLチューニング
  • インデックス追加
  • N+1 解消

といった定番施策も重要ですが、今回 一番効いたのは「無駄なDBアクセスを減らすこと」でした。

今後の改善予定

一定の改善はできたものの、企業・条件によってはまだ改善余地があります。今後は以下の改善を検討しています。

  • SQLそのものの改善
  • 一覧取得時の S3 URL 発行処理の N+1 解消
  • Apollo fetch の活用
  • 待機時間を長く感じさせない UX 改善

We’re Hiring!

燈では、プロダクトの新機能開発はもちろん、SaaSとしての基盤を活かした UX改善・パフォーマンス改善・運用コスト削減 など、“プロダクトを良くし続ける” 取り組みに興味があるエンジニアを募集しています。

興味がある方は、ぜひカジュアル面談でお話しましょう!

akariinc.co.jp