店舗検索のスロークエリを改善してDB負荷を下げた話
はじめに
自社サービスの「店舗検索」ホームページで、ページ表示に極端な時間がかかる問題が発生しました。特にブランド数が多く想定外のデータ数を登録する顧客を導入した際、RDSのCPU使用率が急上昇。調査すると、店舗検索ページの表示に30秒以上(SQLの実行自体も全体で30秒以上) かかっていることが判明しました。
この記事では、原因の特定から クエリ改善 と インデックス追加 の両面で行った対策、その効果、そして学んだことを紹介します。
「店舗検索」ホームページについて
私たちが提供している「カンリーホームページ」は、ユーザーがブランドやエリア、設備などの条件で店舗を探せるホームページです。
検索には店舗に設定されているブランドやエリアに加え、管理画面で用意しているチェック項目(設備や特徴などを表現するフォーマット)も参照していました。
異変に気づいたきっかけ
きっかけは監視システムのCPUアラートでした。ある日突然、RDSのCPU使用率が100%に張り付き、店舗詳細ページの表示にも影響が出そうな状態に。ちょうど冒頭で述べた想定外のデータ数を持つ新しい大規模顧客の店舗データを投入した直後で、検索ページに紐づくSQLが疑わしい状況でした。
調査と最初の対応
まずは EXPLAIN
で実行計画を確認。検索条件で必ず使う company_id
と keyword
に適切なインデックスが効いていないことが分かり、複合インデックスを追加しました。
CREATE INDEX idx_company_keyword
ON company_items(company_id, keyword);
これで一部のクエリは改善されたものの、ページ表示全体では依然として30秒以上。インデックスだけでは足りず、クエリそのものを見直す必要があると判断しました。
当初の仕様と残っていた問題
当初の仕様では「ブランド」「エリア」「チェックボックス」など複数の検索形式に対応する必要があり、実装は「どの形式でも対応できるように、全部まとめてチェックする」方式でした。その後の仕様変更で実際に使う形式が限定されたにもかかわらず、実装は古いまま残っており、ページ表示のたびに不要な形式まで総当たりしている状態に。これが巨大なWHERE句と過剰な JSON_CONTAINS
を生み、ボトルネックになっていました。
改善方針
対策は結果的に2本立てです。
-
インデックス最適化
company_id, keyword
の複合インデックスで入口の絞り込みを高速化。 -
クエリ改善
コンポーネント(検索形式)ごとに本当に必要な条件だけを見るように分岐。ブランドはbrand
/brandGroup
、エリアはlarge
/middle
/small
、その他は同じ形式のデータであるため各コンポーネントを指定して検索。「全部まとめてチェック」は廃止。
改善後のイメージ
改善後は、対象ごとに条件を絞って評価するシンプルな形にしました。
-- ブランド検索
AND (
JSON_CONTAINS(..., '$.brand.id')
OR JSON_CONTAINS(..., '$.brandGroup.id')
)
-- エリア検索
AND (
JSON_CONTAINS(..., '$.area.large')
OR JSON_CONTAINS(..., '$.area.middle')
OR JSON_CONTAINS(..., '$.area.small')
)
-- その他(チェックボックス系)
AND JSON_CONTAINS(..., '$.xxx.items')
改善前はこれらに使わない形式まで加えて巨大な OR 条件で総当たりしていたため、無駄が膨らんでいました。
体感効果
インデックス最適化とクエリ改善を合わせて実施した結果、効果は劇的でした。
- 改善前:店舗検索ページの表示に30秒以上(SQLの実行自体も全体で30秒以上)
- 改善後:SQL実行は0.0942秒まで短縮、ページ表示も体感で一瞬に
CPU負荷も大幅に下がり、RDS全体の安定性が向上。この改善は特定顧客だけでなく 全ての顧客の検索レスポンス改善 につながりました。
実際の効果
改善の効果を、次の観点でも確認しました。
- スロークエリログ:該当のSQLがログにのってこない
- 運用観測:RDSのCPUアラートが解消
学び
-
インデックスは必須だが万能ではない
クエリそのものに無駄があると、インデックスだけでは限界がある。入口(インデックス)と経路(条件式)の両輪が大切。 -
不要になった処理は早めに削る
仕様変更で使わなくなった分岐やパスは、将来のスケール時に致命的なボトルネックになる。継続的なリファクタリングが必要。
まとめ
今回の改善により、店舗検索ページの表示は 30秒以上(SQLの実行自体も全体で30秒以上) → SQL実行は0.0942秒まで短縮、ページ表示も体感で一瞬に。
複合インデックスの追加 と 不要条件の削除(クエリ改善) を組み合わせたことが、三桁レベルの高速化を生みました。
「インデックス最適化」と「クエリの見直し」はどちらも基本ですが、両方やって初めて本質的な改善に到達できる――そんな教訓を得ました。
この教訓をもとに別の機能の改善も成功させており、早速この経験を活かすことができています。
この記事がなにか改善のきっかけになれば幸いです。

株式会社カンリーは「店舗経営を支える世界的なインフラを創る」をミッションに、店舗アカウントの一括管理・分析SaaS「カンリー店舗集客」の開発・提供、他複数のサービスを提供しております。 技術系以外のnoteはこちらから note.com/canly
Discussion