Amazonシステム設計面接体験記:Amazonショッピングカートシステムの設計完全レビュー

システム設計著者: BeautyResume チーム

4年Javaバックエンド経験のAmazonシステム設計面接体験記。Amazonショッピングカートシステムの設計プロセスを詳細解説。MySQL+Redisデュアルストレージ、キャッシュ戦略、カート追加フロー、高同時最適化、ピークイベント対応を網羅

Amazonシステム設計面接体験記:Amazonショッピングカートシステムの設計完全レビュー

背景紹介

2024年4月にAmazonのJavaバックエンド開発職で面接を受けました。経験は4年です。正直に言うと、Amazonのシステム設計面接は私が経験した中で最もハードコアなものでした——面接官はアーキテクチャ図を描かせるだけでなく、データベーススキーマ設計、分散トランザクション、キャッシュ一貫性といった非常に具体的なレベルまで深掘りしました。私が面接したチームはAmazonのコアショッピング体験チームで、面接官はAmazonのショッピングカートシステムの設計を求めました。この問題は一見シンプルに見えますが、深掘りすると高同時読み書き、データ一貫性、分散ロック、キャッシュ戦略、メッセージキューなど多くの技術的ポイントに触れます。約40分話し、20回以上の追及を受けました。以下は完全な振り返りです。

面接プロセスの振り返り

面接官はPrincipal Engineer(L7)で、Amazonショッピング体験アーキテクチャチーム所属と自己紹介しました。面接は55分で、息をつく暇もないタイトなペースでした。

最初の3分:面接官が面接の流れを簡単に説明し、すぐに本題に入りました。「Amazonのショッピングカートに似たシステムを設計してください。」いつものように要件確認から始めました。

3〜12分:要件確認段階。以下の重要な質問をしました:

1. ユーザースケール?——「DAU2億と仮定。Prime Day中はQPSが50万に達する可能性。」

2. カート容量?——「1ユーザー最大120商品。」

3. コア機能?——「カート追加、数量変更、削除、選択/選択解除、カート統合。」

4. 一貫性要件?——「結果整合性で可。強一貫性は不要。」

5. マルチデバイス同期?——「PC、モバイルアプリ、モバイルWebの同期をサポート。」

面接官は「要件の理解が良い。設計を始めましょう」と認めました。

12〜40分:最も核心的なセグメント。ストレージ設計→キャッシュ設計→コアフロー→高同時最適化→スケーリング方案の順で説明。面接官は各段階で非常に具体的な質問を追及し、スムーズに答えられたものと言葉に詰まったものがありました。

40〜50分:面接官はピークイベント時の特別な処理に移りました。「Prime Day中にカートQPSが100倍に急増したらどうする?」「カートは在庫システムとどう連携する?」「カートの価格をどうリアルタイム更新する?」

最後の5分:面接官が質問タイムをくれました。チームの技術的課題とカートシステムの次の進化について聞きました。

実際の問題:Amazonショッピングカートシステムの設計

1. ストレージ設計

カートのストレージはシステム全体の基盤です。MySQL+Redisデュアルストレージ方案を設計しました:

1. MySQLプライマリストレージ:カートテーブル設計——user_id、item_id、sku_id、quantity、checked(選択フラグ)、create_time、update_time。コアインデックス:(user_id, item_id, sku_id)複合一意インデックス。

面接官が「なぜRedisだけでなくMySQLを使うのか?」と質問——1. Redisのデータは損失を保証できず、カートデータはユーザーの資産なので損失は許容できない;2. MySQLは複雑なクエリ(店舗別グループ化など)をサポートし、Redisは複数回のクエリで組み立てる必要がある;3. MySQLはデータ分析や照合に使用できる。

面接官が続けて「ではなぜ全部MySQLにしない?」——MySQLの読み書き性能では50万QPSを支えられないため、Redisでキャッシュ加速が必要。

2. Redisキャッシュ:Hash構造を使用。keyはcart:{user_id}、fieldはitem_id:sku_id、valueはJSON(quantity、checkedなどを含む)。これで1回のHGETALLでユーザーのカート全体を取得でき、String構造の個別GETよりはるかに効率的。

面接官が「なぜStringではなくHash?」——String構造では各商品が別のkeyになり、カート全体の取得にMGETが必要で、key数が多いと性能が低下。Hashは1回のHGETALLで完了し、HINCRBYで原子数量増減もサポート。

2. キャッシュ戦略設計

キャッシュ戦略はカートシステムで最も重要な設計の一つです。読み書き戦略と一貫性保証を説明しました:

1. 書き込み戦略:Write-Behind + 非同期DB永続化。ユーザーがカートに追加する際、まずRedisに書き込み、その後非同期でMySQLに書き込む。面接官が「非同期MySQL書き込みが失敗したらどうする?」——1. MySQL書き込み失敗時は3回リトライ;2. 3回とも失敗したらデッドレターキューに書き込み手動対応;3. RedisとMySQL間で5分ごとに定期照合タスクを実行し、不一致を発見したら修復。

面接官が続けて「照合中にユーザーが不一致のデータを読んだら?」——これはトレードオフであることを認めました。カートシナリオでは短い不一致が許容されます。カートは意思決定支援ツールであり、取引システムではないからです。注文時には在庫システムと価格システムからリアルタイムで最新データを取得し、カート内のデータは参考値です。

2. 読み取り戦略:Cache-Aside。カート読み取り時にまずRedisを確認、ヒットすれば返却;ミスの場合はMySQLを照会しRedisにバックフィル、7日間のTTLを設定。

3. キャッシュブレイクダウン防止:ホットユーザーのカートにはミューテックスロックで同時キャッシュミスを防止。面接官が「ミューテックスロックの実装は?」——RedisのSETNXを使用。ロックを取得したスレッドがMySQLを照会してバックフィルし、他のスレッドは待機後にRedisをリトライ。

3. コアフロー設計

1. カート追加フロー

ユーザーが追加クリック→ゲートウェイ認証→カートサービス→商品有効性チェック(商品サービス呼び出し)→在庫チェック(在庫サービス呼び出し)→Redis書き込み(HSET)→MQメッセージ送信→非同期MySQL書き込み→成功返却。

面接官が「商品有効性と在庫チェックはカート追加の速度を遅くしないか?」——確かにそうなので、これらのチェックを非同期事前チェックに変更:追加時にはチェックせずカートに書き込み、その後非同期でチェック。商品が無効または在庫不足の場合、「無効ステータス」としてマークし、ユーザーがカートを開いた際にグレーアウトしたヒントを表示。これで追加APIのRTを200msから20msに削減。

面接官が「無効な商品を追加したユーザーは苦情を言わないか?」——カートページで「この商品は販売終了」や「在庫切れ」と明示。ほとんどのユーザーは追加後すぐに注文せず、カートを開く頃には非同期チェックが完了し、ステータスは最新。

2. 数量変更フロー

ユーザーが数量変更→カートサービス→Redis HINCRBY(原子増減)→MQメッセージ送信→非同期MySQL更新。重要ポイント:HINCRBYは原子操作で分散ロック不要。

面接官が「ユーザーが急速に+1を連続クリックした場合、数量の不整合は起きないか?」——起きない。HINCRBYは原子操作で、毎回+1が正しく累積される。ただしMQメッセージの順序が乱れる可能性があるため、MySQL更新時は増分ではなく数量の絶対値を使用。

3. カート統合フロー

未ログイン時のカートデータはローカル(Cookie/LocalStorage)に保存され、ログイン後にサーバー側と統合する必要がある。統合戦略:ローカルの各商品をサーバー側と照合し、サーバーに存在する場合は数量の大きい方を採用、存在しない場合は新規追加。

面接官が「統合時の並行競合はどう処理する?」——分散ロック(Redis SETNX)でユーザーのカートをロック。統合中、他の追加リクエストはキューで待機。ロックのタイムアウトは5秒でデッドロックを防止。

4. 高同時最適化

1. ホットユーザーシャーディング:ピークイベント時、特定のトップユーザーのカートアクセスが極めて高い(インフルエンサーによるショッピングシナリオなど)。解決策:これらのユーザーのカートデータをローカルキャッシュに保存し、読み取りリクエストは優先的にローカルキャッシュから、書き込みリクエストはローカルキャッシュとRedisの両方を更新。

2. バッチ操作最適化:ユーザーが全選択/全選択解除する際、Redisを1つずつ更新せずPipelineバッチ実行を使用し、RTをN回のネットワークラウンドトリップから1回に削減。

3. レート制限とデグラデーション:ピークイベント時のカートQPS急増に対し、トークンバケットでレート制限、制限超過時は「システムが混雑しています」を返却。デグラデーション戦略:非コア機能(カート推薦、セット割引提案など)を無効化し、コアの追加・閲覧・削除機能のみを保持。

4. データシャーディング:MySQLはuser_idでシャーディング——16データベース×8テーブル=128テーブル。Redis Clusterはuser_idのハッシュ値でスロットを割り当て。

5. ピークイベント時の特別処理

面接官は特にPrime Dayシナリオについて追及しました:

1. カートと在庫の連携:カート追加は「意図」に過ぎず、在庫はロックしない。在庫は注文時に初めて扣減される。面接官が「カートに表示される在庫はリアルタイムか?」——いいえ、準リアルタイムで、在庫システムから5分ごとに同期。注文時に在庫不足の場合、「在庫切れ」と表示。

2. 価格のリアルタイム更新:カート内の商品価格はプロモーションをリアルタイムに反映する必要がある。方案:カートサービスがMQで価格変更イベントを購読し、受信後にRedisの価格を更新。面接官が「MQメッセージが遅延したら?」——ユーザーがカートを開く際、フロントエンドが価格サービスを呼び出して最新価格を取得するフォールバック方案。

3. カートプレウォーミング:Prime Dayの1時間前に、アクティブユーザーのカートデータをMySQLからRedisに事前ロードし、イベント開始時に大量のキャッシュミスでMySQLが圧倒されるのを防止。

6. スケーリング方案

1. マルチデバイス同期:MQでカート変更イベントをブロードキャストし、各デバイスは受信後にローカルキャッシュをリフレッシュ。面接官が「ユーザーが異なるデバイスで同時にカートを変更したら?」——Last Write Wins。カートシナリオでは競合の確率が極めて低く、競合の影響も限定的。

2. カート推薦:カート内の商品に基づいて関連商品を推薦。方案:カートサービスが推薦サービスを呼び出し、推薦サービスはカート商品の特徴ベクトルで類似推薦を実行。

3. カート共有:カートスナップショットを生成し、友人と共有。方案:カートデータをJSONにシリアライズし、DynamoDBに保存して共有リンクを生成。

心得・アドバイス

1. カートシステムの核心はキャッシュ戦略。面接官は必ずRedis-MySQLの一貫性問題を深掘りする。書き込み戦略(同期Redis書き込み+非同期MySQL書き込み)と照合方案を明確にしておく。

2. ピークイベントシナリオを無視しない。Amazonの面接官は「Prime Dayではどうする?」と好んで聞く。プレウォーミング、レート制限、デグラデーション方案を事前に準備。

3. ストレージ選択の理由を明確に説明する。なぜMySQL+Redisデュアルストレージ?なぜHashでStringではない?各選択に確固たる根拠が必要。

4. 積極的にトレードオフを議論する。「カートデータは短い不一致を許容する。注文時にリアルタイム検証が行われるから」といった表現は非常に評価される。

5. データベーススキーマ設計を準備する。Amazonの面接官はER図の作成やCREATE TABLE SQLの記述を求める可能性がある。カートテーブルのインデックス設計とシャーディング戦略を事前に考えておく。

FAQ

Q:Amazonのシステム設計面接とGoogleの違いは?

A:Amazonはエンジニアリング実装に重点を置き、データベーススキーマ設計、分散トランザクション、キャッシュ一貫性といった非常に具体的なレベルまで深掘りする。Googleはアーキテクチャの考え方に重点を置き、問題分析のフレームワークとトレードオフの議論を重視する。

Q:ショッピングカートシステムのデータ量は?

A:2億ユーザー、平均20商品で約40億件。MySQLシャーディング後、各テーブル約3000万件で合理的な範囲内。

Q:EC経験がない場合は?

A:カートシステムの設計アプローチは汎用的:ストレージ選択→キャッシュ戦略→コアフロー→高同時最適化→スケーリング。他のシナリオ(お気に入り、ToDoリストなど)から類推して説明できる。

#システム設計#Alibaba#Shopping Cart#Amazon#缓存 Strategies#高並行#MySQL#Redis