一休.com Developers Blog

一休のエンジニア、デザイナー、ディレクターが情報を発信していきます

Solr クエリを速度改善したら Solr 全体のパフォーマンスが向上した

この記事は 一休.com Advent Calendar 2023 6日目の記事です。


一休レストランの開発チームでエンジニアをしている香西です。 今回は Solr クエリの速度改善についてお話します。

背景

2023年10月、一休レストランのスマートフォン用 レストラン詳細ページをリニューアルしました! UI/UX の見直しとともに、使用技術も一新しました。

  • バックエンド言語:Python から Rustへ
  • フロントエンドフレームワーク:Nuxt.js から Next.jsへ*1

スマートフォン用 レストラン詳細ページ

課題

「日付を選ぶカレンダーの表示が遅い」

社内限定リリースの直後、多方面からこの声が聞こえてきました...
レストランへ行く日付を選ぶカレンダーは予約フローの第一ステップなので、表示速度が遅いことは致命的です。 特に、設定データ(料理のコース種類・席の種類など)が多いレストランでは、カレンダーの空席状況を取得するのに15秒以上かかることがあり、このままでは正式リリースできない状況でした。

空席カレンダーの UI

一休レストランでは、各レストランの空席情報を全文検索システム Solr にインデックスし、予約できる日を Solr で検索して空席状況を取得しています。 Solr には、レストランの設定データごとにドキュメントを作成しインデックスしています。そのため設定データが多いレストランは検索対象のドキュメント数が膨大になり、検索に時間がかかっていました。

やったこと

検索マイクロサービスを経由するのをやめた

Solr へのアクセスは「フロントエンド → バックエンド → 検索マイクロサービス → Solr」という流れで行われます。 従来からあった検索マイクロサービスのオーバーヘッドが大きかったため、検索マイクロサービスを経由するのをやめて、「バックエンド → Solr」に直接アクセスするようにしました。
検索マイクロサービス(C#)で行われていた Solr クエリの組み立てや、Solr からのレスポンスをオブジェクトに変換し在庫計算を行う処理を、バックエンド(Rust)に移行しました。

ワイルドカードを使うようにした

Solr にインデックスされているデータのなかには、日付ごとに異なる情報が含まれています。これらの情報は、それぞれ特定の日付(例:231025)を含むフィールド名で表現されています。

"231025Close_tdt": "2023-10-21T00:00:00Z",
"231025VisitTimeFrom_tdt": "2023-10-25T18:00:00Z",
"231025VisitTimeTo_tdt": "2023-10-25T18:30:00Z",
"231025HasInventory_b": true,
"231025HasRotationOrBlockTime_b": false,
"231025Inventory_ti": 1,
"231025SalesUpperLimitOver_b": false,

例えば、先1か月分の各日付の情報を取得する場合、以下のような Solr クエリを生成していました。

// 変更前

fl=231025Close_tdt,231025VisitTimeFrom_tdt,231025VisitTimeTo_tdt,231025HasInventory_b,231025HasRotationOrBlockTime_b,231025Inventory_ti,231025SalesUpperLimitOver_b,231026Close_tdt,231026VisitTimeFrom_tdt,231026VisitTimeTo_tdt,231026HasInventory_b,231026HasRotationOrBlockTime_b,231026Inventory_ti,231026SalesUpperLimitOver_b,231027Close_tdt,231027VisitTimeFrom_tdt,231027VisitTimeTo_tdt,231027HasInventory_b,231027HasRotationOrBlockTime_b,231027Inventory_ti,231027SalesUpperLimitOver_b,231028Close_tdt,231028VisitTimeFrom_tdt,231028VisitTimeTo_tdt,231028HasInventory_b,231028HasRotationOrBlockTime_b,231028Inventory_ti,231028SalesUpperLimitOver_b,231029Close_tdt,231029VisitTimeFrom_tdt,231029VisitTimeTo_tdt,231029HasInventory_b,231029HasRotationOrBlockTime_b,231029Inventory_ti,231029SalesUpperLimitOver_b,231030Close_tdt,231030VisitTimeFrom_tdt,231030VisitTimeTo_tdt,231030HasInventory_b,231030HasRotationOrBlockTime_b,231030Inventory_ti,231030SalesUpperLimitOver_b...つづく

field list で「231025のフィールド群,231026のフィールド群,231027のフィールド群 ...」のように、特定の日付が含まれるフィールド群を個別に指定していましたが、日付部分(231025)をワイルドカード(??????)に置き換えて「??????のフィールド群」という書き方に変更しました。

// 変更後

fl=??????Close_tdt,??????VisitTimeFrom_tdt,??????VisitTimeTo_tdt,??????HasInventory_b,??????HasRotationOrBlockTime_b,??????Inventory_ti,??????SalesUpperLimitOver_b

この変更により、設定データが多いレストランではレスポンスタイムが約 1/5 に短縮され、大きな改善効果が得られました!

100件ずつ並列で取得するようにした

最初に、検索結果の総件数のみを取得し、総件数を100で割って何回取得すればよいか判断し、100件ずつ並列で Solr にリクエストを送るようにしました。

Rust で Solr から結果を取得するサンプルコードです。search_calendar にカレンダーの検索条件を渡すと、まず Solr から総件数を取得し、そのあと100件ずつ検索結果を取得します。

pub async fn search_calendar(
    &self,
    input: &model::CalendarInput,
) -> anyhow::Result<Vec<model::Date>> {
    let rows = 100;
    let query = CalendarQuery(input.clone());
    // 先に総件数のみを取得する
    let total_count = self.get_solr_data(&query, 0, 0).await?.response.total_count;
    let query = &query;
    // 100 件ずつ取得する
    let futures = (0..total_count.div_ceil(rows)).map(|n| async move {
        self.get_solr_data(query, n * rows, rows).await
    });
    let res = futures_util::future::try_join_all(futures).await?;
    
    // 以下略(Solr の結果をもとに返り値を作る)
}

不要な Solr クエリを削る

改めて Solr クエリに削除できる部分がないか見直しました。

  • 不要なフィールドを取得していないか
  • ユーザーの指定条件によって削除できるフィールドはないか
  • 無駄に group, sort の機能を使用していないか

といった観点でチェックを行いました。

成果

この改善により、カレンダーの空席状況を取得するのに15秒程かかっていたのが2~3秒程度に短縮され、スムーズな UX をユーザーに提供することができました!

また、システム観点でも大きな効果がありました。 今回の速度改善対象は、スマートフォン用 レストラン詳細ページのカレンダーの検索処理でしたが、Solr 全体のパフォーマンスが向上しました。

  • Solr の CPU コア使用率が半分以上減少
  • Solr 全体のレスポンスタイムが450ミリ秒から200ミリ秒程度に短縮

Solr の CPU コア使用率が半分以上減少

Solr 全体のレスポンスタイムが450ミリ秒から200ミリ秒程度に短縮

一休レストランの Solr に関する改善点はまだ多くありますが、少しずつ着実に取り組んでいきたいと思います。

さいごに

一休では、ともに良いサービスをつくっていく仲間を募集中です!

www.ikyu.co.jp

カジュアル面談も実施しているので、お気軽にご応募ください。

hrmos.co

*1:Next.js で起きた課題については 一休.com Advent Calendar 2023 15日目の記事で解説予定です。