案例研究練習題 (Practice - Case Studies)
Related Concepts
- 13-Case-Studies/01-QR-Code-Generator
- 13-Case-Studies/02-Earthquake-Notification-System
- 13-Case-Studies/03-Polymarket
- 13-Case-Studies/04-Amazon-Price-Tracking
- 13-Case-Studies/05-Tesla-Robotaxi-App
- 13-Case-Studies/06-Spotify-Trending-Songs
- 13-Case-Studies/07-Messenger
- 13-Case-Studies/08-Webhook-Platform
- 13-Case-Studies/09-Google-Docs
- 13-Case-Studies/10-YouTube
- 13-Case-Studies/11-ChatGPT-Tasks
- 13-Case-Studies/12-Airbnb-Booking-Platform
- 13-Case-Studies/13-QA-Support-Agent
- 13-Case-Studies/14-LLM-Inference-API-Service
| 案例 | core tradeoff(一句話) | 招牌 pattern |
|---|---|---|
| QR Code 產生器 | redirect 用 302 而非 301:放棄快取省的延遲,換可改/刪對應 | unique-id + read-heavy cache + CDN |
| 地震通知系統 | geo 用預寫 cell→devices 反向索引而非即時算距離,換 sub-second fan-out | geospatial-cell-index + per-channel fan-out |
| Polymarket | 撮合 append-only 永不回滾,結算失敗只重試,靠 WAL 重建狀態 | in-memory matching + WAL + CQRS + market 分片 |
| Amazon 價格追蹤 | 蒐集用 extension 群眾外包 + 優先 crawler 而非暴力爬,換新鮮度不被 rate limit | prioritized + crowdsourced + CDC |
| Robotaxi 叫車 | 配對狀態放 Redis 共享,DB partial unique index 兜底,換多 worker 強一致 | shared-state cache + per-ride lock + unique 兜底 |
| Spotify 熱門排行 | 用 OLAP 批次(Spark) 而非掃 OLTP,犧牲新鮮度換 42 億筆能算完 | OLTP/OLAP 分離 + batch/stream + top-k 預算 |
| Messenger 即時通訊 | 分片用 chat_id 而非 user_id,換訊息順序唯一來源 + fan-out 降到 M gateway | WebSocket + pub/sub + chat 一致雜湊 |
| Webhook 平台 | 選 at-least-once 而非 exactly-once,要對方冪等,換絕不丟事件 | queue + retry + DLQ + 簽章 |
| Google Docs 協作 | 用 中心化 OT 而非 CRDT,犧牲離線/P2P,換 50–100ms 延遲 + 決定性總序 | OT + WebSocket fan-out + snapshot |
| YouTube 影音 | 影片 bytes 永不過 service(presigned 直傳 S3 + ABR),複雜度推給 client | presigned 直傳 + ABR + 轉碼 DAG + CDN |
| ChatGPT Tasks 排程 | watcher/worker 用 queue 解耦 + at-least-once,job 必須冪等 | poll-and-queue + watcher/worker 分離 |
| Airbnb 訂房 | 用邏輯可用性(reserved + expiry) 而非實體鎖,付款前鎖 inventory | reservation-with-expiry + conditional write |
| Q&A 客服 Agent | 答案必須 grounded 在檢索 context,寧可拒答轉真人 | RAG + hybrid search + intent routing |
| LLM 推論 API | dual-trigger batching:max_wait 湊批,time 設延遲天花板、size 設吞吐地板 | dynamic batching + async req-reply + 背壓 |
Part 1:各案例 signature 自測
每案 1–2 題,直擊該案的 core_tradeoff / 招牌決策。先憑記憶答,再展開核對。
Q1(recall):13-Case-Studies/01-QR-Code-Generator 的 redirect 為什麼回 302 而不是 301?這個取捨犧牲了什麼、換來什麼?
301(Permanent) 會被瀏覽器快取、未來可能直接跳過我們的 server;302(Temporary)不被快取,每次都經過 server。選 302 是為了讓擁有者能隨時改/刪對應 URL——犧牲「省下一跳的延遲」,換取「每次掃描拿到最新狀態 + 可控」。(唯一 token 走 SHA-256 → Base62 → 取前 N 字元,碰撞靠 DB qr_token UNIQUE 重產重試。)
Q2(application):13-Case-Studies/01-QR-Code-Generator 是 read-heavy(write:read ≈ 1:100)。它怎麼把唯一 token 算出來又不必每次查 DB,碰撞如何兜底?
對輸入做 SHA-256 → Base62 編碼 → 取前 N 字元(N 看 key space,10 億約取 6 字,62^6 ≈ 568 億)。因為純雜湊是 deterministic,可不查 DB 直接算;唯一性交給 DB 的 UNIQUE constraint,碰撞時報錯就重產重試。要讓同輸入得到不同碼則在輸入加 secret/nonce。
Q3(recall):13-Case-Studies/02-Earthquake-Notification-System 做地震圈內裝置比對時,為什麼不在 alert 當下即時算每台裝置的 lat/long 距離?它改用什麼?
改用預先寫好的 cell → devices 反向索引:平時就把裝置位置歸到 geospatial cell,alert 時只要對受災範圍 polyfill 出涵蓋的 cells、直接撈那些 cell 的裝置即可。犧牲精確比對與少量邊界 false positive,換取 alert 路徑短到能 sub-second fan-out。
Q4(application):13-Case-Studies/03-Polymarket 的撮合引擎在「鏈上結算失敗」時為什麼絕不 rollback 已完成的 off-chain fill?這帶來什麼好處?
撮合一旦完成就 append-only,結算失敗只重試 / 升級 ops 處理,不回滾。放棄鏈下鏈上的強原子性,換取撮合的 deterministic ordering,以及狀態可由 WAL 完整重建——in-memory matching engine 崩了也能 replay 還原,且讀路徑靠 CQRS fan-out、按 market_id 分片。
Q5(recall):13-Case-Studies/04-Amazon-Price-Tracking 為什麼不暴力爬遍 5 億頁,而是用 browser extension 群眾外包 + 優先式 crawler 的 hybrid?
暴力全爬覆蓋慢、又會被 Amazon rate limit 卡死。Hybrid 蒐集讓熱門商品自然被使用者瀏覽時優先抓、新鮮度高;犧牲覆蓋均勻性與資料純淨度,換取優先級對齊需求 + 不被封鎖。價格變動再走 CDC/事件驅動通知,讀路徑做預聚合。
Q6(application):13-Case-Studies/05-Tesla-Robotaxi-App 的配對狀態為什麼不放 stateless worker 記憶體,而是集中放 Redis?最終的「1 AV : 1 Ride」靠什麼兜底?
多個 worker 必須共享配對狀態,放單一 worker 記憶體就無法協調。所以把 matching state + per-ride lock 放 Redis 讓所有 worker 共用(過程一致靠 Redis),最後再用 Ride DB 的 partial unique index 做最終結果兜底(結果一致靠 DB)。犧牲純記憶體的簡單與一點延遲,換多 worker 下的強一致。
Q7(recall):13-Case-Studies/06-Spotify-Trending-Songs 為什麼用 OLAP + 批次(Spark)pipeline 而不是直接掃 OLTP?什麼時候該升級成 streaming?
直接掃 OLTP 會拖垮線上寫入,且 42 億筆聚合在 OLTP 上算不完。用 OLTP/OLAP 分離 + CDC 進 lakehouse + 批次聚合,犧牲分鐘級新鮮度換「算得完且不影響線上」。要 < 1 分鐘新鮮度就升級到 Flink streaming,犧牲簡單與離線重算便利換即時。top-k 結果預算並快取。
Q8(recall):13-Case-Studies/07-Messenger 為什麼用 chat_id 而非 user_id 做分片單位?這放棄了什麼直覺、換到什麼?
放棄「以使用者為中心」的直覺,改用 chat_id 分片(pub/sub):同一 chat 永遠由單一 owner 處理 → 訊息順序有唯一真實來源(單調 message id),且 fan-out 從「N 個接收者」降到「M 個 gateway」。gateway 維持 stateless + WebSocket 雙向、離線走 store-and-forward。
Q9(recall):13-Case-Studies/08-Webhook-Platform 為什麼選 at-least-once 而不是 exactly-once?代價由誰承擔?
選 at-least-once:接收端暫時掛掉也絕不丟事件,且系統更簡單。代價是客戶可能收到重複投遞,所以要求對方做冪等。投遞走 queue 解耦 + retry + DLQ(多次失敗進死信),payload 簽章供驗證。
Q10(application):13-Case-Studies/09-Google-Docs 為什麼選中心化 OT 而不是 CRDT?這個選擇的延遲與一致性後果是什麼?
用單一序列器 owner 做 OT:犧牲離線編輯 / P2P / 高併發上限,換取每按鍵 50–100ms 的極低延遲與決定性總序。代價是有單點 owner(需一致雜湊路由 + failover),靠 snapshot compaction 控制重建成本,viewer fan-out 用 queue 解耦。
Q11(recall):13-Case-Studies/10-YouTube 的「影片 bytes 永不經過我們的 service」具體怎麼做?複雜度被推去哪?
client 用 presigned URL 直傳 S3(multipart/可續傳),播放用 manifest + 多 format segments 做 ABR 自適應串流,把 GB 級流量推離 app server 換 scale。代價是複雜度攤到 client(ABR、multipart 進度)與離線轉碼 DAG pipeline;分發靠 CDN edge offload,metadata 水平分片。
Q12(recall):13-Case-Studies/11-ChatGPT-Tasks 為什麼把 watcher(查詢)與 worker(執行)用 queue 解耦、並接受 at-least-once?前提條件是什麼?
解耦能吸收突發流量、crash 容錯、watcher/worker 各自水平擴展。選 at-least-once 而非 exactly-once 換掉精準一次的語意簡單性——前提是 job 必須 idempotent。排程走 poll-and-queue + 時間桶分區,週期性任務由 outbox 驅動。
Q13(application):13-Case-Studies/12-Airbnb-Booking-Platform 為什麼用「邏輯可用性(reserved + expiration_time)」而不是對 inventory 上實體鎖?它犧牲了什麼乾淨性?
訂房時把房態標成 reserved 並帶 expiration_time(conditional write),付款前就鎖住 inventory、又不長持 DB lock。犧牲的是需要一條 cron 補償回收過期 reservation 的路徑、以及「狀態即時翻回」的乾淨性——但 cron 延遲也不影響正確性。讀路徑靠預聚合 + cache,搜尋靠 Elasticsearch CDC。
Q14(recall):13-Case-Studies/13-QA-Support-Agent 為什麼寧可主動拒答「轉接真人」,也不讓 LLM 用預訓練知識補答?這是什麼取捨?
答案必須完全 grounded 在檢索到的 context;檢索不到就拒答轉真人,不讓模型用 parametric 知識編。用「會拒答的準確」換掉「什麼都答的流暢」。實作走 RAG + hybrid search/rerank + intent routing 平行檢索 + 對話感知改寫 query,回應用 streaming 壓低 TTFT。
Q15(recall):13-Case-Studies/14-LLM-Inference-API-Service 的 dual-trigger batching 是什麼?兩個 trigger 各管什麼?
動態批次同時有兩個觸發條件:time trigger(max_wait ≈ 50ms) 給單請求延遲設天花板,到時就算沒湊滿也送批;size trigger 給 GPU 吞吐設地板,湊滿一批就立刻送。用人為延遲換 GPU batch 填充率。請求走 async request-reply + request-id 關聯,按 queue 深度 auto-scale,過載時背壓 / load shedding。
Part 2:跨案例比較(cross-case,本次解鎖)
案例的價值在「同一道設計軸上不同案做了不同選擇」。以下全為 [analysis],答案直接點名具體案例。
Q16(analysis):read-heavy + unique-id 群——13-Case-Studies/01-QR-Code-Generator 的「算 ID + 唯一性靠 DB constraint 兜底 + cache/CDN 擋讀」這套,能不能原封不動套到 13-Case-Studies/04-Amazon-Price-Tracking?哪一招可直接搬、哪一招不行?
可直接搬的:read 路徑的 cache + 預聚合(兩者都讀遠多於寫)。
不能直接搬的:13-Case-Studies/01-QR-Code-Generator 的核心難點是「產生全域唯一短碼」(SHA-256→Base62 + UNIQUE 兜底);13-Case-Studies/04-Amazon-Price-Tracking 沒有「發新 ID」這個問題——它的商品 id 是既有的,真正的難點在資料蒐集(群眾外包 extension + 優先 crawler 避開 rate limit)與價格變動偵測(CDC + 事件通知)。所以 unique-id 那一招對 Price-Tracking 用不上;只有「read-heavy 用 cache 擋」這條軸是共通的。
Q17(analysis):real-time 推送群——13-Case-Studies/07-Messenger、13-Case-Studies/02-Earthquake-Notification-System、13-Case-Studies/03-Polymarket、13-Case-Studies/05-Tesla-Robotaxi-App 分別該用 SSE / WebSocket / push notification 哪一種?為什麼方向性需求決定了選擇?
關鍵軸是「單向 vs 雙向」與「裝置是否在線/前景」:
- 13-Case-Studies/07-Messenger:WebSocket(雙向)——使用者既收又發訊息,需持久雙向連線;離線時退化成 store-and-forward + push。
- 13-Case-Studies/02-Earthquake-Notification-System:push notification——目標裝置多半 App 不在前景甚至螢幕關閉,必須走 OS 級 push 才喚得醒;走 per-channel fan-out。
- 13-Case-Studies/03-Polymarket:單向下行串流(SSE / WebSocket 推 price 更新)——大量 viewer 只需收即時報價,讀路徑用 CQRS pub/sub fan-out。
- 13-Case-Studies/05-Tesla-Robotaxi-App:對 AV 端用 persistent connection RPC(雙向、下指令也收回報),對乘客端則是行程狀態的下行更新。
口訣:要回寫 → WebSocket / persistent RPC;只下推給在線 viewer → SSE;要喚醒離線/背景裝置 → push notification。
Q18(analysis):誰需要 sharding、誰不需要,為什麼?對照 13-Case-Studies/01-QR-Code-Generator、13-Case-Studies/07-Messenger、13-Case-Studies/03-Polymarket、13-Case-Studies/10-YouTube。
分片與否取決於「容量/熱點」還是「正確性」是瓶頸:
- 13-Case-Studies/01-QR-Code-Generator:不需要——10 億 × 200B ≈ 200GB 單機放得下,瓶頸在讀流量與容錯,用 read replica + failover 即可。
- 13-Case-Studies/07-Messenger:需要,且分片鍵是 chat_id——不是為了容量,而是讓「同一 chat 單一 owner」成立,順帶把 fan-out 收斂到 gateway。
- 13-Case-Studies/03-Polymarket:需要,按 market_id 分片——每個市場的撮合是獨立的 in-memory engine,分片讓各 market 並行且狀態可獨立用 WAL 重建。
- 13-Case-Studies/10-YouTube:metadata 水平分片——量級巨大且讀寫都重;但影片 bytes 根本不進 DB(直傳 S3 + CDN),所以分片只解 metadata。
結論:容量裝得下就別分(QR);為一致性/owner 模型分(Messenger、Polymarket);為純量級分 metadata(YouTube)。
Q19(analysis):contention / 一致性群——13-Case-Studies/03-Polymarket、13-Case-Studies/12-Airbnb-Booking-Platform、13-Case-Studies/05-Tesla-Robotaxi-App 各自用「樂觀 / 悲觀 / 分散式鎖 / 邏輯鎖」的哪一種來處理競爭?為什麼合適?
- 13-Case-Studies/03-Polymarket:單執行緒序列化(in-memory matching engine)——根本不靠鎖,把同一 market 的撮合塞進單一 owner 串行處理 + WAL 持久化,達成 deterministic ordering。這是「用 single-writer 消滅 contention」。
- 13-Case-Studies/12-Airbnb-Booking-Platform:樂觀 / 邏輯鎖(reservation + expiry + conditional write)——不長持 DB 實體鎖,靠 conditional write 搶 reserved 狀態,過期由 cron 回收。適合「下單到付款有人類思考時間、不能長持鎖」。
- 13-Case-Studies/05-Tesla-Robotaxi-App:分散式鎖 + 唯一約束兜底——per-ride lock 放 Redis 給多 worker 協調(過程),Ride DB partial unique index 做最終保證(結果)。適合「多無狀態 worker 競爭同一資源、且要 1:1 強一致」。
軸:single-writer 序列化(Polymarket)< 樂觀/邏輯鎖(Airbnb)< 分散式鎖 + DB 兜底(Robotaxi),依「contention 強度與是否要即時 1:1」遞增。
Q20(analysis):blob / CDN 群——13-Case-Studies/10-YouTube 的影片 bytes 與 13-Case-Studies/01-QR-Code-Generator 的 QR 圖像都是 static blob 走 CDN,但兩者的「上傳 / 分發」複雜度差在哪?
共通:都把 static bytes 放 object store + CDN edge offload,不讓 app server 扛流量。
差異:
- 13-Case-Studies/01-QR-Code-Generator 的 QR 圖像小且不可變——生成一次即可,CDN 純粹快取靜態檔;沒有 client 直傳、沒有轉碼。
- 13-Case-Studies/10-YouTube 的影片是 GB 級且需多版本——必須 client presigned URL 直傳 S3(multipart/可續傳)繞過 service,再走轉碼 DAG 產生多 format/解析度 segments + manifest 做 ABR。複雜度被刻意攤到 client 與離線 pipeline。
一句話:QR 是「快取一張靜態小圖」;YouTube 是「直傳繞過 service + 離線轉碼 + 自適應串流」——同樣靠 CDN,但 YouTube 多了直傳與轉碼兩層。
Q21(analysis):LLM 群——13-Case-Studies/11-ChatGPT-Tasks、13-Case-Studies/13-QA-Support-Agent、13-Case-Studies/14-LLM-Inference-API-Service 在「queue / batching / overload 保護」上各自的重點不同,怎麼分工?
三者切在 LLM 服務的不同層:
- 13-Case-Studies/11-ChatGPT-Tasks:重點在排程與解耦——watcher/worker 用 queue 分離、at-least-once + job 冪等,解決「定時觸發的突發 + crash 容錯 + 各自擴展」。它不在乎 GPU 填充率,在乎可靠觸發。
- 13-Case-Studies/14-LLM-Inference-API-Service:重點在 batching 與背壓——dual-trigger dynamic batching 拉高 GPU 利用率,按 queue 深度 auto-scale,過載時 backpressure / load shedding。它是「把 GPU 榨乾且不被打爆」的那一層。
- 13-Case-Studies/13-QA-Support-Agent:重點在檢索品質與 grounding——RAG + hybrid search/rerank + intent routing,overload 保護主要靠 streaming TTFT 改善體感而非批次。它在乎答得對不對,不在乎 GPU batch。
一句話:Tasks 管「何時叫模型」、Inference API 管「怎麼餵 GPU 又不爆」、Q&A Agent 管「餵什麼 context 才答得準」。
Q22(analysis):reliable-delivery 群——13-Case-Studies/08-Webhook-Platform 與 13-Case-Studies/07-Messenger 都要「不丟訊息」,但對「重複」與「順序」的容忍度不同,導致機制差在哪?
- 13-Case-Studies/08-Webhook-Platform:選 at-least-once——可接受重複(要求接收方冪等),不強調跨事件全域順序;機制是 queue + retry + DLQ + payload 簽章。目標是「對外第三方端點掛掉也絕不丟事件」。
- 13-Case-Studies/07-Messenger:同樣不能丟,但順序是第一公民——靠 chat_id 單一 owner + 單調 message id 給每個 chat 唯一序,離線走 store-and-forward 補投。它在乎「使用者看到的訊息順序正確」,重複可由 message id 去重。
軸:都不丟(at-least-once 心法),但 Webhook 把重複丟給對方冪等、順序不保證;Messenger 用 single-owner + 單調 id 把順序鎖死,並用 id 去重。
| 設計軸 | 對照案例與不同選擇 |
|---|---|
| unique-id 產生 | 只有 QR 真的要「發唯一短碼」(hash+Base62+UNIQUE 兜底);Price-Tracking 沒這問題,難點在蒐集 |
| read-heavy 擋讀 | QR / Price-Tracking / Airbnb / Spotify 共通:cache + 預聚合(+ CDN) |
| real-time 推送 | WebSocket(Messenger 雙向)/ persistent RPC(Robotaxi)/ SSE 下行(Polymarket)/ push(Earthquake 喚醒離線) |
| 要不要 sharding | 裝得下別分(QR 200GB);為一致性/owner 分(Messenger chat_id、Polymarket market_id);為量級分 metadata(YouTube) |
| contention 處理 | single-writer 序列化(Polymarket)< 樂觀/邏輯鎖(Airbnb reservation+expiry)< 分散式鎖+DB 兜底(Robotaxi) |
| blob / CDN | QR:快取靜態小圖;YouTube:presigned 直傳繞過 service + 轉碼 DAG + ABR,再 CDN |
| LLM 三層 | Tasks 管排程解耦;Inference API 管 batching/背壓榨 GPU;Q&A Agent 管 RAG grounding 答得準 |
| reliable delivery | 都 at-least-once 不丟:Webhook 靠對方冪等+DLQ、不保序;Messenger 靠 single-owner+單調 id 鎖死順序 |
| 一致性的最終兜底 | 過程靠 cache/lock,結果靠 DB 約束:Robotaxi(partial unique index)、QR(UNIQUE constraint) |