1
/
5

【TECH BLOG】SQL ServerのCDCを用いた加熱商品の販売イベントにおける負荷軽減の取り組み

こんにちは、SRE部の廣瀬です。

本記事では、ZOZOTOWNでカートに商品を入れる際に使われているデータベース群の内、SQL Server(以降、カートDBと呼ぶ)にフォーカスします。ZOZOTOWNでは数年前から、人気の商品(以降、加熱商品と呼ぶ)が発売された際、カートDBがボトルネックとなる問題を抱えています。様々な負荷軽減の取り組みを通じて状況は劇的に改善されていますが、未だに完璧な課題解決には至っていません。

そこで今回は、加熱商品の発売イベントにおける負荷軽減の取り組みを振り返ります。また、直近の取り組みとして、SQL ServerのCDCを用いた新たな負荷軽減の検証内容をご紹介します。

背景 - カートDBのボトルネックについて

加熱商品の発売イベントに関する対策について、最初に言及した記事としては以下が挙げられます。この記事では人気の福袋商品を加熱商品として紹介していますが、それ以外にも加熱商品は様々な種類のものが存在します。


ZOZOTOWNで最大級のトラフィックを記録する福袋発売イベントで実施した負荷対策 - ZOZO TECH BLOG
こんにちは。開発部の廣瀬です。 本記事では、昨年障害が発生してしまったZOZOTOWNの福袋発売イベントについて負荷対策を実施し、今年の福袋イベント期間を無傷で乗り切った話をご紹介したいと思います。 ...
https://techblog.zozo.com/entry/sqlserver-tuning-luckybag


上記記事を参考に、加熱商品の発売イベントにおけるカートDBのボトルネックについてご説明します。

ZOZOTOWNのカート投入の仕様

ZOZOTOWNでは、「カートに入れる」ボタンを押したタイミングで在庫が確保されます。つまり、カートに入った商品はそのまま注文完了まで進めば、確実に購入できます。ECサイトによっては、「カートに入れる」ボタンを押したタイミングでは在庫が確保されません。代わりに、注文完了後に順次在庫の確保処理を実行して、在庫が確保できない場合はキャンセルのお詫びメールを送信する仕様になっています。ZOZOTOWNのカート投入の仕様は現実世界でのショッピングの体験を再現しており、個人的に好きな仕様の1つです。本仕様により、「カートに入れる」ボタンを押したタイミングで以下のようなクエリ(以降、在庫更新クエリと呼ぶ)がカートDBに対して実行されます。


update 在庫テーブル set 在庫数 = 在庫数 - 1 where PK = ***


SQL Serverの論理リソース競合について

SQL Serverでは、データを更新する際に様々なリソースに対して排他制御をかけます。様々な排他制御の内、本記事で言及する「行ロック」と「ページラッチ」について簡単に説明します。

「行ロック」は、行(レコード)に対して読み書きする際に獲得する必要のある論理的なリソースです。この仕組みによって「1つのレコードを一度に更新できるのは、1つのクエリだけ」といったルールを実現できます。

「ページラッチ」は、複数のレコードを格納している8KBの物理領域に対して読み書きする際に獲得する必要のある論理的なリソースです。基本的には「行ロック」も「ページラッチ」も、同一リソースへの書き込み(以降、writeと呼ぶ)は競合し、同一リソースへの読み取り(以降、readと呼ぶ)は競合しません。

表にまとめると以下の通りです。

       write/write read/write read/read
行ロック   競合する 競合する 競合しない
ページラッチ 競合する 競合する 競合しない

競合が発生すると、片方のクエリはもう片方のクエリが「行ロック」や「ページラッチ」を解放するまで待たされることになります。つまり、論理リソースの競合が発生するということは該当クエリの実行時間の遅延につながるということです。

なお、SQL Serverのロックについては以下の記事で詳しくまとめていますので、良かったらご覧ください。


SQL Serverのロックについて出来る限り分かりやすく解説 - Qiita
公式ドキュメントだと文字だけの情報なので、図解することで分かりやすく理解してもらえるように説明してみました。 わかり辛かったらすみません! DB上でデータを操作(SELECT/INSERT/UPDATE/DELETE等)する際、データの整合性を保つために使われる排他制御の仕組み。 例えば、「1つのレコードを一度に更新できるのは、1つのクエリだけ」といったルールを実現してくれる。 ...
https://qiita.com/maaaaaaaa/items/38fd95b142b07acf7700


加熱商品の発売イベントにおけるDB論理リソース競合

通常時は、様々な商品がカートに投入されている状況のため、複数の在庫更新クエリが同時に同一リソースへ更新要求を出すことはほとんどありません。したがって、「行ロック」も「ページラッチ」も大幅なクエリ遅延につながるような競合は発生しません。

しかし、加熱商品の発売イベントでは、特定の人気商品に対して在庫更新クエリが集中します。このような状況下では大量の「行ロック」および「ページラッチ」競合が発生し、クエリの実行時間の大幅な遅延やクエリタイムアウトエラー多発に繋がってしまいます。ワーストケースでは、クエリの遅延によりワーカースレッドが枯渇して、カートDB全体のスループットが著しく下がるという障害が発生することもあります。

このリソース競合を図示すると以下のようになります。



カートDBのボトルネックまとめ

ここまでの内容をあらためてまとめます。

  • ZOZOTOWNでは「カートに入れる」ボタンを押したタイミングで在庫が確保され、内部的には在庫更新クエリが発行されている
  • 在庫更新クエリを実行するためには「行ロック」および「ページラッチ」という論理リソースを獲得する必要がある
  • 加熱商品の発売イベントでは、特定の商品にカート投入要求が集中し、DB内部で「行ロック」「ページラッチ」関連の論理リソース競合が多発する
  • 論理リソース競合多発によってクエリの処理時間が遅延しタイムアウトエラー多発や、ワーカースレッド枯渇によるDB全体のスループット激減という障害につながることもある

このように、CPU負荷やディスク負荷の高騰といった物理リソース起因ではなく、SQL Server内部で獲得する必要のある論理リソース競合がボトルネックである点が特徴的となっています。

次は、カートDBのボトルネックに対するこれまでの対応策を振り返っていきます。

カートDBボトルネック対策の歴史

1.在庫分割による排他制御の分散

こちらの記事で紹介しているように、加熱商品の論理在庫を分割することで、在庫更新クエリによる排他制御を分散させる案です。

この対応のイメージ図は以下の通りです。



この案を2018年に実装して以降、2015年から3年連続で障害が発生していた福袋発売イベントを無障害で乗り切れています。一方で、以下のような課題も抱えていました。

  • 運用負荷が大きく、年に1回の福袋発売イベント時だけ発動していた
  • 効果は限定的で、分割するメリットが無いほどの少ない在庫数に対しては適用できない

他にもクエリチューニングを実施する等の様々な対策を施してきましたが、限界を迎えていました。具体的には、対策を入れて上昇していくDBの処理能力を、加熱商品の発売イベント時のトラフィックがさらに上回るようになっていきました。

そこでSQL Serverのレイヤだけで対応するのではなく、ワークロードを加味した別DBの選定等、課題の根本的な解決を目指すことになりました。成果の第一弾として、2021年にカート決済機能リプレイスのPhase1がリリースされましたので、そちらをご紹介します。

2.キューイングシステムの導入によるキャパシティコントロール

これまでのカートDBでは、在庫更新クエリ数が増えれば増えるほど、カートDBへのリクエスト数も増える状況になっていました。そこで、カート決済機能リプレイスのPhase1という位置づけで、カートDBの前段にキューイングシステムを設置しました。これにより、在庫更新クエリが増えても、キューイングシステムの後段に位置するカートDBへの更新リクエスト数を一定に保つことが可能となりました。

キューイングシステムの概略図は以下の通りです。



より詳しい情報は下記のテックブログ達にまとまっております。よろしければご覧ください。


ZOZOTOWN カート決済機能リプレイス Phase1 〜 キャパシティコントロールの実現 - ZOZO TECH BLOG
こんにちは。ECプラットフォーム部 カート決済ブロックの高橋です。 ZOZOTOWNでは、数年前よりClassic ASPからJavaへのリプレイスが実施されています。そのリプレイスの一環として、2021年4月からカート決済機能のマイクロサービス化を開始しました。 ...
https://techblog.zozo.com/entry/zozotown-cart-and-payment-system-replacement-phase1


ZOZOTOWN カート投入の分散キューイングシステム 〜 プロダクションレディまでの歩み - ZOZO TECH BLOG
こんにちは。家系らーめん好きが高じて鶏油を自分で取得するようになり、金色に輝く液体を見るだけで パブロフの犬 的に涎が止まらない、SRE部の横田です。普段はSREとしてZOZOTOWNのリプレイスや運用に携わっています。 先日、弊社の高橋が執筆したZOZOTOWNカート機能リプレイスに関する記事が公開されました。 ...
https://techblog.zozo.com/entry/production-ready-zozotown-cart-queueing-system


ZOZOTOWNカート機能のリプレイスPhase1裏側を大公開 - ZOZO TECH BLOG
こんにちは、カート決済部の佐藤です。普段はZOZOTOWNカート決済サービスの新機能開発、既存改修、運用保守を担当しております。 弊社はモノリスからマイクロサービスへのリプレイスを進めており、カート決済サービスも先日リプレイスPhase1の記事を掲載いたしました。 ...
https://techblog.zozo.com/entry/zozotown-cart-replacement-phase1-inside-story


How Amazon DynamoDB supported ZOZOTOWN's shopping cart migration project | Amazon Web Services
In this post, we show you a case study of an e-commerce site that had relational database management system (RDBMS) performance problems and how Amazon DynamoDB contributed to their solution. ZOZO has large-scale sale events requiring engineers to monitor
https://aws.amazon.com/jp/blogs/database/how-amazon-dynamodb-supported-zozotowns-shopping-cart-migration-project/


この対応を入れたことで、下図のようにレイテンシ、エラー率の両面で劇的な効果を上げることができました。



ここまで、カートDBボトルネック対策の歴史を振り返りました。ワークアラウンドな対応にとどまっていた「在庫分割による排他制御の分散」と比べて、「キューイングシステムの導入によるキャパシティコントロール」は、あらゆる加熱商品に有効な負荷軽減の取り組みとなりました。しかし、ここまでの対策を実施しても論理リソース競合によるクエリタイムアウトの多発という障害の発生を完全に無くすことはできませんでした。

次項では、現状のシステム構成でも障害が発生してしまう要因を説明します。

現在のカートDBが抱えるボトルネック

現在のカートDBのボトルネックに関するワークロードの概略図を以下に示します。



在庫テーブルに対しては、常にwriteとreadの両方のリクエストが発生しています。writeは基本的に在庫更新クエリのみであり、キューイングシステムの導入によってキャパシティをコントロール可能な状態になっています。一方で、readは様々なリクエストで発生し、各リクエスト毎に同時実行数も異なります。例えば秒間10000リクエストのreadクエリもあれば、秒間10リクエストのreadクエリもあります。また、大半のreadはキューを経由しないため、アクセス増加に伴って秒間リクエスト数も増加していきます。

readとwriteはページラッチの競合が発生するため、writeのリクエスト数を一定に保ったとしてもreadの数が増えるほどページラッチ競合の発生リスクは増加していきます。したがって、readの増加によるreadとwriteのページラッチ競合が現在のカートDBのボトルネックということになります。なお、上述の内容は以下の記事に記載されている方法で調査を行い特定しました。よろしければご覧ください。


SQL Serverの障害調査フローと事例のご紹介~原因不明な障害の調査から解決まで~ - ZOZO TECH BLOG
こんにちは。ECプラットフォーム部の廣瀬です。 弊社ではサービスの一部にSQL Serverを使用しています。今回は2020年度に発生していたSQL Serverに関連する障害について、調査から対策実施までの流れを紹介したいと思います。これまでも弊社テックブログにて、SQL Serverに関するトラブルシューティングをいくつか紹介してきました。 ...
https://techblog.zozo.com/entry/sqlserver-incident-management-2020


トラブルの原因特定率を劇的に向上させるSQL Serverロギングの仕組み作り - ZOZO TECH BLOG
こんにちは。アーキテクト部の廣瀬です。 私は2021年7月に、Data Platformカテゴリにおいて Microsoft MVP を受賞しました。昨年に続き2度目の受賞です。これからも受賞し続けられるように引き続きがんばります。 弊社ではサービスの一部にSQL Serverを使用しています。以前テックブログで SQL Serverの障害調査フローをご紹介しました。その中で ...
https://techblog.zozo.com/entry/sqlserver-logging


次項では、現状のボトルネックを踏まえた負荷軽減の取り組みについてご紹介します。

直近でのカートDB負荷軽減の取り組み

現状のボトルネックを踏まえて、システムを以下の構成に変更することで論理リソース競合を軽減できるのではと考えました。

続きはこちら

株式会社ZOZOからお誘い
この話題に共感したら、メンバーと話してみませんか?
株式会社ZOZOでは一緒に働く仲間を募集しています
1 いいね!
1 いいね!

同じタグの記事

今週のランキング

株式会社 ZOZOさんにいいねを伝えよう
株式会社 ZOZOさんや会社があなたに興味を持つかも