Slide 1

Slide 1 text

Spring Boot+Redis Cache 検索APIにキャッシュを導入、実装時の工夫や効果 2022年3月23日 ZOZO Tech Meetup 株式会社ZOZO 技術本部 検索基盤部 検索基盤ブロック 佐藤 由弥 Copyright © ZOZO, Inc. 1

Slide 2

Slide 2 text

© ZOZO, Inc. 株式会社ZOZO 技術本部 検索基盤部 検索基盤ブロック 佐藤 由弥 ● 2020年4月新卒として入社(今年4月で4年目) ● 入社当時はJava未経験 ○ 検索マイクロサービスの開発や運用業務を通じて習得 ● 最近の主な業務は検索ページのフロントエンドリプレイス ○ レガシーコードをJavaに置き換え ● 扱う言語やサービス:Java・Python・VBS・Elasticsearch・BigQuery ● 趣味:ゲーム、最近はホグワーツに入学 2

Slide 3

Slide 3 text

© ZOZO, Inc. Agenda 1. ZOZOTOWNの検索について 2. キャッシュの導入 3. 実装時の工夫 4. まとめ 5. おまけ(フロントエンドリプレイスの話) 3

Slide 4

Slide 4 text

© ZOZO, Inc. Agenda 1. ZOZOTOWNの検索について 2. キャッシュの導入 3. 実装時の工夫 4. まとめ 5. おまけ(フロントエンドリプレイスの話) 4

Slide 5

Slide 5 text

© ZOZO, Inc. 5 突然ですが!

Slide 6

Slide 6 text

© ZOZO, Inc. 6 ZOZOTOWNの検索と聞いて何を思い浮かべますか?

Slide 7

Slide 7 text

© ZOZO, Inc. 以下を思い浮かべた方、正解です! 7 ZOZOTOWNの検索について 世代別ランキング 類似画像検索 サジェスト パーソナライズ化された商品の並び順 これ以外にも沢山あるよ 検索フォーム

Slide 8

Slide 8 text

© ZOZO, Inc. 8 ZOZOTOWNの検索を支えるシステム構成 ● 検索API:Elasticsearchから商品情報を受け取り、ZOZOTOWNへ検索機能を提供 ● インデクシングバッチ:DBからElasticsearchへ商品情報を定期的に同期するバッチ

Slide 9

Slide 9 text

© ZOZO, Inc. 検索システムとして大切な要素 9 今日話すこと ● 速度 ● 精度 ● 可用性 ● 再現性などなど

Slide 10

Slide 10 text

© ZOZO, Inc. 検索システムとして大切な要素 10 今日話すこと ● 速度 ○ 検索APIにキャッシュ導入 ● 精度 ● 可用性 ● 再現性などなど 本資料ではここを深掘る

Slide 11

Slide 11 text

© ZOZO, Inc. Agenda 1. ZOZOTOWNの検索について 2. キャッシュの導入 3. 実装時の工夫 4. まとめ 5. おまけ(フロントエンドリプレイスの話) 11

Slide 12

Slide 12 text

© ZOZO, Inc. 12 キャッシュ導入に至った背景

Slide 13

Slide 13 text

© ZOZO, Inc. 13 検索APIにキャッシュを導入した背景、その1 マイクロサービス化に伴い検索APIの直接参照の機会が増加、負荷面に懸念が出てきた

Slide 14

Slide 14 text

© ZOZO, Inc. 14 検索APIにキャッシュを導入した背景、その2 検索APIのABテスト実施にあたりWebサイト側の修正も必要、改善サイクルが回りづらい

Slide 15

Slide 15 text

© ZOZO, Inc. 15 キャッシュ導入のシステム全体像

Slide 16

Slide 16 text

© ZOZO, Inc. 16 ZOZOTOWNの検索を支えるシステム構成(キャッシュ導入後) Amazon ElastiCache for Redisを追加 キャッシュのやり取りはJavaで実装

Slide 17

Slide 17 text

© ZOZO, Inc. 17 キャッシュ導入による効果

Slide 18

Slide 18 text

© ZOZO, Inc. 18 ZOZOTOWNの検索を支えるシステム構成(キャッシュ導入後) Elasticsearchを経由せず、高速に検索結果を返却可能

Slide 19

Slide 19 text

© ZOZO, Inc. 19 キャッシュ導入による効果(工夫を含めた最終的な効果) ● 速度改善に効果あり ○ 最終的に殆どのエンドポイントで 50ms~100msほどレイテンシが低下 ○ 特に効果があったエンドポイントはp99で94%改善 ● 安定した速度で検索結果の提供が可能に ○ レイテンシの振れ幅が少なくなり安定した 例)世代別ランキングAPIのレイテンシの様子 ※ この後紹介する様々な工夫を取り入れた後の 最終的なレイテンシの低下を表しています 90%以上改善

Slide 20

Slide 20 text

© ZOZO, Inc. Agenda 1. ZOZOTOWNの検索について 2. キャッシュの導入 3. 実装時の工夫 4. まとめ 5. おまけ(フロントエンドリプレイスの話) 20

Slide 21

Slide 21 text

© ZOZO, Inc. 21 実装時の工夫 ● エンドポイントごとにTTLを最適化 ○ エンドポイントごとにTTLを延長してレイテンシを計測 ○ 最適な値になるまで上記を繰り返す ● キャッシュキーの最適化 ○ セールの開始時にキャッシュが切り替わるように キャッシュキーにタイムセクションを追加 ○ Cache Stampedeの対策 ● キャッシュのGZIP圧縮 ○ 検索結果をそのままキャッシュすると ネットワークI/Oやストレージが逼迫 ここを深掘る リンク:https://techblog.zozo.com/entry/implement-cache-in-search-microservice 割愛:詳細はテックブログ参照ください

Slide 22

Slide 22 text

© ZOZO, Inc. 22 キャッシュのGZIP圧縮の概要 ● どんな課題があった? ○ 登録するキャッシュは、商品の様々な情報を含むためデータサイズが大きい ○ RedisのネットワークI/Oやストレージの使用量が増加 ● なにが嬉しい? ○ Redisに保存するデータを圧縮することで、 ネットワークI/Oやストレージの使用量を減らし、検索APIの高速化を図る ● この後の話 ○ 実際のJavaのコードを交えて、どのようにGZIP圧縮したか詳細を説明

Slide 23

Slide 23 text

© ZOZO, Inc. 23 Spring bootからRedisにキャッシュを保存するまでの流れ Redisはインメモリデータストアのため、オブジェクトをデータ形式に変換して保存が必要 扱うRedisクライアント:spring-boot-starter-data-redis https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data- redis ● シリアライズ/デシアライズは JdkSerializationRedisSerializerクラスで行われる ● オブジェクトのシリアライズを可能にするには 追加する Redisクライアント Redis シリアライズ 検索結果オブジェクト バイト配列 キャッシュ保存 デシリアライズ キャッシュ読み取り バイト配列 検索結果オブジェクト 同 じ 検索API Icon made by flaticon from https://www.flaticon.com/

Slide 24

Slide 24 text

© ZOZO, Inc. 24 キャッシュのGZIP圧縮 シリアライズ gzip 圧縮 約60%~80%圧縮 GZIP圧縮の流れ ● serialize() ○ オブジェクトをシリアライズするメソッド ○ isGzipEnabledでGZIP実行有無を制御 ● compress() ○ バイト配列をGZIP圧縮するメソッド ○ GZIPOutputStreamにByteArrayOutputStreamを 渡すことで出力先はバイト配列になる 検索結果オブジェクト バイト配列 圧縮されたバイト配列 Icon made by flaticon from https://www.flaticon.com/

Slide 25

Slide 25 text

© ZOZO, Inc. 25 キャッシュのGZIP圧縮 シリアライズ gzip 圧縮 約60%~80%圧縮 GZIP圧縮の流れ ● serialize() ○ オブジェクトをシリアライズするメソッド ○ isGzipEnabledでGZIP実行有無を制御 ● compress() ○ バイト配列をGZIP圧縮するメソッド ○ GZIPOutputStreamにByteArrayOutputStreamを 渡すことで出力先はバイト配列になる 検索結果オブジェクト バイト配列 圧縮されたバイト配列 Icon made by flaticon from https://www.flaticon.com/

Slide 26

Slide 26 text

© ZOZO, Inc. 26 キャッシュのGZIP圧縮 シリアライズ gzip 圧縮 約60%~80%圧縮 GZIP圧縮の流れ ● serialize() ○ オブジェクトをシリアライズするメソッド ○ isGzipEnabledでGZIP実行有無を制御 ● compress() ○ バイト配列をGZIP圧縮するメソッド ○ GZIPOutputStreamにByteArrayOutputStreamを 渡すことで出力先はバイト配列になる 検索結果オブジェクト バイト配列 圧縮されたバイト配列 Icon made by flaticon from https://www.flaticon.com/

Slide 27

Slide 27 text

© ZOZO, Inc. 27 キャッシュのGZIP圧縮 GZIP解凍の流れ gzip 解凍 デシリアライズ ● deserialize() ○ Redisから取得した圧縮データを復元するためのメソッド ○ バイト配列からオブジェクトに変換 ● decompress() ○ 圧縮されたバイト配列を解凍するメソッド ○ データの解凍と解凍されたデータをバイト配列に書き込む ○ 一連の流れは煩雑化しやすいので IOUtilsクラスのcopyメソッドを使い簡略化 検索結果オブジェクト バイト配列 圧縮されたバイト配列 Icon made by flaticon from https://www.flaticon.com/

Slide 28

Slide 28 text

© ZOZO, Inc. 28 キャッシュのGZIP圧縮 GZIP解凍の流れ gzip 解凍 デシリアライズ ● deserialize() ○ Redisから取得した圧縮データを復元するためのメソッド ○ バイト配列からオブジェクトに変換 ● decompress() ○ 圧縮されたバイト配列を解凍するメソッド ○ データの解凍と解凍されたデータをバイト配列に書き込む ○ 一連の流れは煩雑化しやすいので IOUtilsクラスのcopyメソッドを使い簡略化 検索結果オブジェクト バイト配列 圧縮されたバイト配列 Icon made by flaticon from https://www.flaticon.com/

Slide 29

Slide 29 text

© ZOZO, Inc. 29 キャッシュのGZIP圧縮 GZIP解凍の流れ gzip 解凍 デシリアライズ ● deserialize() ○ Redisから取得した圧縮データを復元するためのメソッド ○ バイト配列からオブジェクトに変換 ● decompress() ○ 圧縮されたバイト配列を解凍するメソッド ○ データの解凍と解凍されたデータをバイト配列に書き込む ○ 一連の流れは煩雑化しやすいので IOUtilsクラスのcopyメソッドを使い簡略化 検索結果オブジェクト バイト配列 圧縮されたバイト配列 Icon made by flaticon from https://www.flaticon.com/

Slide 30

Slide 30 text

© ZOZO, Inc. 30 キャッシュのGZIP圧縮 ● spring-boot-starter-data-redis ○ RedisTemplateが用意されている ○ Redisとのデータ操作が簡略化 ● RedisTemplate ○ デフォルトシリアライザーの JdkSerializationRedisSerializerをラップ したクラスに差し替える Redisを扱うための依存関係 RedisTemplateにてラップしたクラスに差し替える 元のRedisTemplate ※ テンプレートが異なる場合、適宜差し替え場所を変える

Slide 31

Slide 31 text

© ZOZO, Inc. 31 キャッシュ圧縮による効果 ● ElastiCacheメモリ使用率が約1/3に減少 ● ネットワーク通信量が約1/7に減少 懸念していたCPU使用率は変化なし ○ 約200KBの文字列で構成されたレスポンス情報の圧縮なので サイズが小さく負荷がかからなかった ○ リソースが十分に与えられたPod上で実行されているため 影響が少なかった キャッシュ圧縮によって、以下の効果が得られた ElastiCacheメモリ使用率 ネットワーク通信量 CPU使用率 約1/3減少 約1/7減少

Slide 32

Slide 32 text

© ZOZO, Inc. Agenda 1. ZOZOTOWNの検索について 2. キャッシュの導入 3. 実装時の工夫 4. まとめ 5. おまけ(フロントエンドリプレイスの話) 32

Slide 33

Slide 33 text

© ZOZO, Inc. 33 まとめ/学んだこと まとめ ● 速度改善の一環で検索APIにキャッシュを導入 ● 殆どのエンドポイントで50~100msほどレイテンシが低下 ● GZIPによるキャッシュ圧縮/解凍の実装方法を紹介 ● ストレージやネットワーク使用量が低下 学んだこと ● Java完全理解 ○ ほぼ未経験でしたが、実装をある程度任せてもらえたので理解が深まった ● レイテンシをより意識するように ● 闇雲な改善は良くない ○ 計測→仮説→実装の流れが大切

Slide 34

Slide 34 text

© ZOZO, Inc. Agenda 1. ZOZOTOWNの検索について 2. キャッシュの導入 3. 実装時の工夫 4. まとめ 5. おまけ(フロントエンドリプレイスの話) 34

Slide 35

Slide 35 text

© ZOZO, Inc. 35 おまけ(フロントエンドリプレイスの話) これまでのZOZOTOWNの課題 ● Webサーバの技術スタックがレガシー ○ Classic ASP・jQuery・ES5 ○ 他言語でよくある便利機能は自作が必要で開発速度が鈍化 ● ビューロジックが独自テンプレートエンジンで実現 ○ コードが独特で癖があり実装コストが高い ○ ビューとAPI側にビジネスロジックが分散 リプレイス後のZOZOTOWN ※ 説明のため簡略化しています

Slide 36

Slide 36 text

© ZOZO, Inc. 36 最後に 「検索のバックエンドに興味ある方」や「リプレイスに興味ある方」を募集しています カジュアル面談もあるので気軽にどうぞ! 求人ページ:https://hrmos.co/pages/zozo/jobs/0000034