Airbnb 訂房平台 (Design Airbnb Booking Platform)

一句話定位 + TL;DR(含 35 分鐘心智時鐘)

設計 Airbnb 的 訪客訂房流程:依 城市 + 日期區間 搜尋房源、查看房源詳情、在指定日期 預訂。系統有 雙人格——搜尋 / 看房是 read-heavy、availability-first(要快、要撐旺季);訂房是 consistency-first絕不能 double booking)。
招牌取捨:訂房用 「邏輯可用性」(reserved + expiration_time) 取代實體鎖。不用悲觀鎖長持 transaction(5 分鐘付款窗會吃爆 DB、引發 deadlock),也不依賴 cron 即時翻狀態;而是把「是否可訂」算成 status=available OR (status=reserved 且已過期) 的邏輯判斷,靠 單一 conditional write(atomic transaction) 搶位。cron 只是事後清理實體狀態,它延遲了也不影響正確性——這就是本題的決勝洞察。其餘決勝點:inventory 以 (home_id, date) 為粒度預先產生未來 6–12 個月(把搜尋變成 range scan)、Elasticsearch + CDC 解搜尋延遲、cache + stateless 水平擴展 撐 400K QPS。
配速(35 min 預算):~5 min 釐清需求 → ~4 min 容量估算 → ~3 min API + ~5 min 資料模型(inventory 是地基)→ ~6 min 高階架構(三條 flow)→ ~9 min deep dive(double booking 是重頭戲)→ ~4 min 擴展瓶頸 → ~2 min 收尾。開場先一句話複述、宣告「搜尋走 availability、訂房走 consistency」這條主軸,並把 double booking 標記為「壓軸 deep dive」——宣告配速與主軸本身就是高分動作。

1. 釐清需求與範圍 (Clarify & Scope ~5 min)

開場後先框邊界,別急著畫架構。Airbnb 是巨型系統,主動縮小範圍(聚焦訪客訂房)本身就是成熟度信號。建議問題:

釐清問題 為何要問 / 影響到哪個決策 本題假設(依來源)
聚焦誰的視角?host 還是 guest? 決定範圍大小與核心 flow 聚焦 guest 訂房流程
搜尋條件是什麼?地點怎麼表示? 決定搜尋索引與 query 形狀 city name + date range(地點先用城市名簡化)
房源詳情要含 images / reviews 嗎? 決定是否要 blob / CDN / 評論子系統 省略 image / review,只留文字資訊
訂房對一致性的要求?能容忍重複訂嗎? 直接決定本題核心(鎖 / 一致性設計) 絕不可 double booking → consistency-first
搜尋的延遲要求? 決定要不要 Elasticsearch / 預算索引 search latency < 500ms
規模 / 流量型態?有旺季尖峰嗎? 驅動估算與擴展設計 要撐 peak season,read-heavy
付款要自己做嗎? 決定是否引入第三方 / 法規範圍 委託第三方(Stripe / Adyen),不自建
defer 話術

怎麼避免 double booking 是這題的靈魂,我先標記為壓軸 deep dive——現在先把 booking 當成『檢查 inventory→寫 booking→串付款』的端到端流程畫出來,等高階架構成形後,再回頭把『並發搶位』這塊做深。」把最難的點推遲到 deep dive,先保住敘事連貫。

2. 功能性與非功能性需求 (FR / NFR 與設計北極星)

設計北極星(NFR 直接導出後續所有選型的判準):

NFR 導出的設計北極星 後續對映選型
搜尋 availability + <500ms 讀路徑避開 full scan,能 range scan / 打索引 / 打 cache inventory 預算化、Elasticsearch、cache
訂房 consistency / 不重複訂 訂房必須是原子的『搶位』,且不長持鎖 conditional write + reserved/expiration
撐旺季尖峰 stateless 水平擴展 + cache 擋讀 stateless service、Redis cache、read replica
雙人格(讀快 / 寫準) 讀寫路徑解耦、各自最佳化 Search Service vs Booking Service 拆開
一句話北極星

「這是一個 讀路徑要快(availability)、寫路徑要準(consistency)的雙人格系統」——搜尋與訂房各走一條優化線,後面每個選型都能從這句推回去。

3. 容量估算 (Back-of-Envelope ~4 min)

估算的目的是 驅動 cache / 索引 / 擴展決策(估算 → 選型的 hand-off)。回連 02-Distributed-Systems/05-Numbers-to-Know

讀流量(瀏覽 / 搜尋 QPS)

假設 10M DAU,每人平均瀏覽 1 小時 (= 3600 s)
同時在線概念 QPS ≈ 10,000,000 × 3600 ÷ 86,400 ≈ 417,000 QPS
                 (源頭概算取整為 ~400K;read-heavy,主戰場)

寫流量(訂房 QPS)

訂房是低頻動作(旅客一次旅程訂 1~數次)
write throughput 相對讀 << 1%  → 寫不是吞吐瓶頸
但寫的「正確性」(no double booking) 才是難點,不是吞吐

Inventory 儲存(預算化的代價)

inventory 以 (home_id, date) 為一筆
N homes × 未來 ~365 天/home  → 列數 = N × ~365
(預先產生未來 6–12 個月的 date rows)
→ 寫入與儲存成本變高,換來搜尋變成簡單 range scan

下表 QPS / 列數皆為 推導值(由 DAU × 瀏覽時長、homes × 預產天數算出,來源僅給出 10M DAU、1 小時瀏覽、6–12 個月預產等輸入)。

維度 數字 對後續決策的意義
瀏覽 / 搜尋 QPS ≈ 400K QPS(推導值) 必須靠 cache + stateless 水平擴展 扛,不能每次打 DB
訂房 QPS 遠低於讀(推導值) 寫吞吐不是瓶頸;並發正確性 才是難點
Inventory 列數 N homes × ~365(推導值) 預算化把搜尋變 range scan,代價是寫入 / 儲存成本上升
讀寫比 極度 read-heavy 讀路徑是所有快取 / 擴展的重心
估算的最大收穫

400K QPS 全是讀 → 訂房的難點 不是吞吐而是並發正確性。這把面試重心精準導向 deep dive:搜尋解「延遲 / 吞吐」,訂房解「double booking」。

4. 用到的積木 / Building Blocks Used

架構即目錄:先給導覽圖,再展開。下表每個決策都對映既有 concept note(無 alias 連結);本表即「本案例練到哪些積木」的單一事實來源(single source of truth)。

設計決策 積木 在本題的作用(≤1 行)
容量估算驅動選型 02-Distributed-Systems/05-Numbers-to-Know 算出 ~400K 讀 QPS,決定 cache + stateless 擴展
讀寫優先級對立(CAP 取捨) 02-Distributed-Systems/02-CAP-Theorem 搜尋偏 AP、訂房偏 CP,同系統內按路徑切
服務入口路由 11-Infrastructure/01-API-Gateway API Gateway 把請求分派到 Home / Search / Booking Service
REST endpoint 設計 03-API-Design/02-REST 以資源(home / booking)為中心定義搜尋 / 詳情 / 訂房
訂房不可 double booking 12-Ops-and-Reliability/01-Dealing-With-Contention 並發搶同一 inventory,核心是 contention 處理
原子搶位(單筆 conditional write) 05-Database-Advanced/01-Transactions 用 transaction 把「檢查可用 + 標 reserved」做成原子操作
悲觀鎖的替代評估 09-Messaging-Coord/04-Distributed-Lock 對照悲觀鎖 / 分散式鎖,本題刻意不長持鎖
搜尋避免 full scan 04-Database-Indexing/01-Indexing-Overview 對 city / date 建索引,減少掃描列數
全文 / 複雜搜尋 08-Search-and-AI/03-Elasticsearch inverted index + fuzzy match 把搜尋推到 <500ms
搜尋引擎與主庫同步 09-Messaging-Coord/01-Data-Pipeline CDC 把 Postgres 變更串流到 Elasticsearch
read-heavy 快取 07-Caching-Storage/01-Caching home data 變動少,home_id→home_object 高 hit rate
快取選型 / 失效 07-Caching-Storage/04-Redis Redis 存熱門 home;DB trigger invalidate 避免 stale
讀擴展三層卸載 10-Design-Patterns/01-Scaling-Reads cache + read replica 擋住 400K 讀
stateless 水平擴展 02-Distributed-Systems/03-Scalability 各 service 無狀態,旺季加機器即擴容
容錯與讀擴展 05-Database-Advanced/03-Replication read replica 分攤搜尋 / 看房讀流量
進階地理搜尋 04-Database-Indexing/05-Geospatial-Index landmark 半徑搜尋用 PostGIS / Redis GEO
第三方付款委託 03-API-Design/05-API-Security 委託 Stripe/Adyen,避免自存卡號的合規 / 安全負擔

5. API 設計 (API Design ~3 min)

由 FR 驅動,採 REST 資源風格(理據見 03-API-Design/02-REST)。

Method Path 用途 重點
GET /home/search?city={city}&startDate={s}&endDate={e}&pageSize={n}&page={p} 搜尋房源 走 Search Service,回 Home[]支援 pagination
GET /home/:homeId 取得房源詳情 { id, city, address, type, amenities[] },可走 cache
POST /home/book?homeId={id}&startDate={s}&endDate={e} 預訂房源 走 Booking Service → 檢查 inventory → 串第三方付款
細節話術

搜尋 endpoint 內建 pagination——400K QPS 規模下,回傳整批 listings 會撐爆頻寬與延遲;分頁讓每次 query 都是有界的 range scan。

6. 資料模型與儲存選型 (Data Model & Storage Choice ~5 min)

核心是 三張表,其中 Inventory Table 是整題地基(搜尋與訂房都圍著它轉),故獨立成段。

Home (房源詳情,read-heavy / 變動少)
--------------------------------------------------
home_id     PK
city        VARCHAR  (INDEX,搜尋條件)
address     VARCHAR
type        VARCHAR
amenities   JSON/ARRAY
--------------------------------------------------

Inventory (★地基★,粒度 = 每間房每一天)
--------------------------------------------------
(home_id, date)   複合 PK          ← 一房一天一列
status            ENUM  available | reserved | booked
expiration_time   TIMESTAMP        ← 僅 reserved 時有意義(搶位倒數)
INDEX (city, date) / (home_id, date)  ← 把搜尋變 range scan
--------------------------------------------------
列數 = N homes × 未來 6~12 個月天數  (需 cron 定期向前預產)

Booking (訂單)
--------------------------------------------------
booking_id   PK
home_id      FK
guest_id     FK
start_date / end_date
status        ENUM  created | paid
created_at
--------------------------------------------------

儲存選型:

選項 適配本題嗎 理由
關聯式 DB(PostgreSQL) ✅ 主選 訂房需 ACID transaction + conditional write;inventory 是結構化、要強一致;天生適合
Elasticsearch(搜尋輔助) ✅ 輔助 搜尋走 inverted index + fuzzy,<500ms;經 CDC 與 Postgres 同步
Redis(cache) ✅ 輔助 home data 變動少,home_id→home_object 高 hit rate;進階亦可做 geo
NoSQL KV(如 DynamoDB) △ 也行 可承載,但訂房的多列原子 check-and-set 在關聯式更直接(見 deep dive)
inventory 預算化(precompute)的取捨

把 inventory 攤成 (home_id, date) 每天一列、預先產生未來 6–12 個月,是本題一個刻意取捨:搜尋因此變成簡單的 WHERE city=? AND date IN range AND status=available 的 range scan + filter(換來 <500ms),代價是 寫入與儲存成本上升(要持續 cron 向前補新日期的列)。這是「用空間 / 寫入換讀延遲」的典型。

→ 積木:DB 選型理據見 06-Database-Tech/02-PostgreSQL;搜尋走 08-Search-and-AI/03-Elasticsearch;索引見 04-Database-Indexing/01-Indexing-Overview;快取見 07-Caching-Storage/04-Redis

7. 高階架構 (High-Level Design ~6 min)

把積木串成端到端。三條 flow(看房 / 搜尋 / 訂房),再看 scaled 架構。

(A) 看房詳情 flow(read-heavy / availability-first)

Client ── GET /home/:homeId ──► API Gateway ──► Home Service
                                                    │ 1. 先查 Cache:home_id → home_object ?
                                                    │     ├─ HIT  → 直接回
                                                    │     └─ MISS → 查 Home Table → 回填 cache
                                                    ▼
                                          回 { id, city, address, type, amenities }
   (home data 變動少 → hit rate 高;DB trigger 在更新時 invalidate cache 避免 stale)

(B) 搜尋 flow(availability-first / <500ms)

Client ── GET /home/search?city&dateRange&page ──► API Gateway ──► Search Service
                                                                       │
                                                                       ▼
                                          查 Inventory:WHERE city=? AND date IN [range] AND status=available
                                              (複合 index → range scan + filter,非 full scan)
                                                                       │
                                                                       ▼
                                                            pagination → 回 Home[]
   (規模放大時改打 Elasticsearch,主庫經 CDC 同步)

(C) 訂房 flow(consistency-first / 不可 double booking)

Client ── POST /home/book?homeId&dateRange ──► Booking Service
   │
   │ 1. 【原子搶位】BEGIN TXN:
   │      檢查 date range 內每列  status=available  OR (reserved 且已過期)
   │      若全可用 → UPDATE status=reserved, expiration_time = now + 10min
   │      新增 Booking (status=created)
   │    COMMIT TXN        ← 任一列搶不到 → rollback,回「已被訂」
   │
   │ 2. 導向第三方 Payment Service (Stripe / Adyen) 輸入卡號
   │
   ▼ 3. Payment 回成功:
        Booking.status = paid
        Inventory.status = booked
      Payment 失敗 / 逾時:
        Inventory 回 available(或等過期 + cron 清理)

(D) Scaled 架構(端到端)

                       ┌──────────────────────────────────────────────┐
   Client ───────────► │                 API Gateway / LB              │
                       └───────┬───────────────┬───────────────┬──────┘
                               │               │               │
                       ┌───────▼────┐  ┌───────▼─────┐  ┌──────▼──────┐
                       │Home Service│  │Search Service│  │Booking Svc  │  ← 皆 stateless,旺季加機器
                       │(availability)│ │(availability)│  │(consistency)│
                       └──┬─────┬───┘  └──────┬───────┘  └──────┬──────┘
                          │     │             │                 │
                  ┌───────▼─┐ ┌─▼────────┐ ┌──▼───────────┐     │ 原子 conditional write
                  │ Redis   │ │Read       │ │Elasticsearch │     │
                  │ Cache   │ │Replicas   │ │(inverted idx)│     │
                  └─────────┘ └────┬──────┘ └──────▲───────┘     │
                                   │               │ CDC          ▼
                          ┌────────▼───────────────┴─────────────────────┐
                          │  PostgreSQL  (Home / Inventory / Booking)      │
                          │  Write node ──(replication)──► Read Replicas   │
                          └───────────────────────────────────────────────┘
                                            ▲
                          Cron job:(a) 向前預產未來日期的 inventory 列
                                    (b) 清理過期 reserved → available
                          Payment:Booking Svc ─► 第三方 (Stripe/Adyen) ─► callback

關鍵 hand-off:Gateway 按 path 分派三個 stateless service → 讀路徑(Home/Search)打 cache / replica / Elasticsearch;寫路徑(Booking)走 write node 做原子 conditional write;搜尋引擎靠 CDC 與主庫解耦同步。

8. 深入探討 (Deep Dives ~9 min)

不重複原則(link-don't-restate)

通用機制(transaction 隔離、cache 策略、CDC、inverted index 原理)只在 concept note 解釋;這裡只講「本題怎麼用、選哪個、為什麼」。任一機制超過 ~3 行就砍掉改連結。本題無 home note 的新材料(邏輯可用性 vs 實體狀態、inventory 預產、付款委託)才在此 inline 教學並登記到 new_concepts_introduced

8.1 並發訂房:如何在不長持鎖的前提下避免 double booking(壓軸)

問題:多位使用者在 重疊日期 搶同一 home,最終只能一人成功;且付款窗很長(~分鐘級),不能讓使用者「付完款才發現被搶」。
為何是瓶頸:付款是慢的外部步驟。若用悲觀鎖把 inventory 鎖到付款回來,DB 會被長持 transaction 拖垮;若不鎖,又會 double booking。

方案 做法 取捨
A.(不佳)Pessimistic Locking SELECT FOR UPDATE 鎖住 inventory rows,直到付款回應才釋放 長持 transaction(~5 min lock)吃爆 DB 資源lock contention / deadlock(A 鎖 09/01-02、B 鎖 09/04-05 互等);Postgres 無原生 txn 內 lock timeout,要 app 層管理
B.(較好)reserved + expiration + cron booking 時把列改 reservedexpiration=now+10min;付款成功→booked、失敗→available;cron 掃過期 reserved 翻回 available 鎖窗短、不長持 txn;但 過期與 cron 執行之間有固有時間落差,且 cron 故障 / 延遲會嚴重影響訂房
C.(最佳)邏輯可用性 > 實體狀態 把「可訂」定義成 status=available OR (status=reserved 且 expired);單一原子 txn 內:檢查此邏輯條件 → 改 reserved, expiration=now+10min → commit cron 仍存在但 只做事後實體清理即使 cron 延遲,系統行為也正確(搶位判斷不依賴 cron 翻好狀態)

選擇與理由:採 C。關鍵洞察是 「一張 ticket 的真實可用性 = 實體狀態 + 是否過期 的組合,是個邏輯判斷,不是某個欄位的當下值」。把它做成 單筆原子 conditional write(在一個 transaction 裡同時驗證邏輯可用 + 標 reserved),就不需要:(1) 長持鎖等付款;(2) 依賴 cron 即時翻狀態。cron 仍要跑(過期 reservation 不會自動翻回 available),但它 延遲了也不影響正確性——這正是 logical availability > physical status 的精髓,也是真實 booking 系統的常見做法。這是本題的 core_tradeoff。
→ 積木:12-Ops-and-Reliability/01-Dealing-With-Contention05-Database-Advanced/01-Transactions、對照 09-Messaging-Coord/04-Distributed-Lock

新材料:邏輯可用性 vs 實體狀態(本案例首次 introduce → 已登記 new_concepts_introduced)

一般人直覺把 inventory 的 status 欄位當「唯一真相」,於是必須有人(cron)即時把過期的 reserved 翻回 available,否則房源「卡住沒人能訂」。邏輯可用性 反過來:在 讀取判斷時就 inline 算過期——available := (status=='available') OR (status=='reserved' AND now > expiration_time)。如此 真相在判斷邏輯裡,不在欄位的瞬時值裡,cron 只是把實體欄位「對齊」到邏輯狀態以利清理 / 報表,它的延遲不會讓任何房源被卡住或被重複訂。

8.2 搜尋延遲:從 full scan 到 <500ms

問題SELECT home_id FROM Inventory WHERE city=? AND start_date=? AND end_date=? 隨 homes 成長會變成 full table scan,越來越慢;未來還要支援多欄位查詢。
為何是瓶頸:400K 讀 QPS 下,每次掃全表既慢又貴,直接撞穿 <500ms 紅線。

手段 做什麼 取捨
對 city / date 建索引 把欄位值對映到列,減少掃描列數;配合避免 SELECT *、用 LIMIT 減回傳量 幾乎無腦上;但 每個 index 拖慢寫入(寫時要同步更新),需在 index 數與寫效能間平衡
Elasticsearch inverted index 把每個詞對映到出現的 documents → 快速定位;支援 fuzzy / 部分匹配 強大且擅長高流量複雜查詢;但 要與主庫經 CDC 同步(複雜、要保一致)、多一套 cluster 的維運成本

選擇與理由先建 index 保底(任何查詢不全表掃描),當搜尋需求變複雜 / 流量更大時 引入 Elasticsearch。ES 經 CDC(Change Data Capture) 連到 authoritative store(Postgres / DynamoDB),把變更串流同步到搜尋索引——主庫負責真相、ES 負責搜尋,兩者解耦。代價是同步可靠性與 cluster 維運成本,需要可靠的 CDC pipeline。
→ 積木:04-Database-Indexing/01-Indexing-Overview08-Search-and-AI/03-Elasticsearch09-Messaging-Coord/01-Data-Pipeline

8.3 撐旺季:400K 讀 QPS 怎麼扛

問題:旺季 ~400K QPS 全是讀,不能每個 request 都打 DB。
為何是瓶頸:read-heavy 的典型死法——讀流量壓垮 DB。

選擇與理由(1) cache layer——home_id→home_object,因 home data 變動少 → hit rate 高;miss 才打 DB 並回填;用 DB trigger invalidate cache(home 更新時失效,避免 stale)。(2) stateless service——三個 service 都無狀態,旺季 直接水平加機器(3) read replica 分攤搜尋 / 看房讀流量。寫路徑(訂房)吞吐低,不是瓶頸,但要保 consistency(見 8.1)。
→ 積木:07-Caching-Storage/01-Caching07-Caching-Storage/04-Redis10-Design-Patterns/01-Scaling-Reads02-Distributed-Systems/03-Scalability05-Database-Advanced/03-Replication

8.4 進階地理搜尋(landmark 半徑)

問題:目前只支援「依城市」搜尋。若要「某地標 X 公里內的 homes」呢?

方案 Pros Cons
1. 第三方地圖 SDK / API 方便直接,MVP 首選 用量大時成本高
2. Geospatial DB(PostGIS) 不需新 service / 外部依賴 實作較費時;讀流量高時要加 read replica / cache 常見查詢
3. 獨立 cache + geo index(Redis GEO) in-memory 回應快 額外費用 + 維運;Airbnb 寫吞吐不高,未必值得

選擇與理由:實務上 Option 1 → Option 2只有當 geo query 成為核心瓶頸才上 Redis GEO。因為 Airbnb 的 write throughput 不高,獨立 geo cache 的成本未必划算——能說「先評估便宜方案、別過早上重武器」本身就是成熟度信號。
→ 積木:04-Database-Indexing/05-Geospatial-Index(同源延伸閱讀:13-Case-Studies/02-Earthquake-Notification-System 的 geospatial 設計,一般推論

8.5 付款為什麼委託第三方

問題:要不要自建付款?
選擇與理由:除非公司有專門支付部門,否則 基於 compliance 委託 Stripe / Adyen——它們有更高等級的安全標準保存卡號,替我們扛下 PCI-DSS 等合規負擔。Booking Service 只負責把使用者導去付款頁、接 callback 更新狀態,自己不碰卡號
→ 積木:03-API-Design/05-API-Security

9. 擴展與瓶頸 (Scaling & Bottlenecks ~4 min)

主動找出系統會先在哪裂開並補強。

瓶頸 / 風險 補強手段 積木
旺季讀流量壓垮 DB cache(home_id→object,高 hit rate)+ read replica 分攤讀 07-Caching-Storage/01-Caching05-Database-Advanced/03-Replication
搜尋 full scan 變慢 index → Elasticsearch(inverted index + CDC 同步) 08-Search-and-AI/03-Elasticsearch09-Messaging-Coord/01-Data-Pipeline
並發訂房 double booking 邏輯可用性 + 單筆原子 conditional write(不長持鎖) 12-Ops-and-Reliability/01-Dealing-With-Contention05-Database-Advanced/01-Transactions
cron 故障 / 延遲導致房源卡住 邏輯可用性把正確性與 cron 解耦——cron 只做事後清理,延遲不影響搶位 12-Ops-and-Reliability/01-Dealing-With-Contention
inventory 列無限增長 / 未來日期沒列 cron 定期向前預產 未來 6–12 個月的 (home_id, date) 10-Design-Patterns/02-Scaling-Writes
service 算力不足 stateless → 水平加機器,前面擺 Gateway / LB 02-Distributed-Systems/03-Scalability11-Infrastructure/01-API-Gateway
熱門城市 / 熱門 home(hot partition) 熱門 home data 進 cache / 多副本;搜尋結果分頁 + 邊快取 07-Caching-Storage/04-Redis10-Design-Patterns/01-Scaling-Reads
ES 與主庫不一致 可靠 CDC pipeline;主庫為真相,ES 僅供搜尋 09-Messaging-Coord/01-Data-Pipeline
drive 的關鍵句

「系統會 在兩處先裂開:讀路徑(400K QPS 全打 DB)→ 用 cache + replica + ES 卸載;訂房並發(double booking)→ 用邏輯可用性 + 原子 conditional write。寫吞吐很低,所以 訂房的難點是正確性不是吞吐。」先指出最可能裂開處再補強,比逐項羅列更有說服力。

10. 取捨總表與收尾 (Trade-off Recap & Wrap-up ~2 min)

決策點 選擇 放棄了什麼 為什麼值得
訂房並發控制 邏輯可用性 + 原子 conditional write 悲觀鎖的直覺 / 狀態即時翻新的乾淨 不長持鎖、付款前就鎖位、cron 延遲不影響正確(招牌取捨
inventory 結構 (home_id, date) 每天一列、預先產生 較低的寫入 / 儲存成本 搜尋變 range scan,換到 <500ms
搜尋引擎 index 保底 → Elasticsearch + CDC ES cluster 維運 / 同步複雜度 inverted index + fuzzy,撐高流量複雜查詢
讀擴展 cache + read replica + stateless 額外複雜度 / 成本 read-heavy 400K QPS 的硬需求
一致性策略 搜尋 availability / 訂房 consistency 全系統統一一致性模型的簡單 雙人格:讀要快、寫要準,各取所需
付款 委託第三方(Stripe/Adyen) 自建付款的掌控度 合規 / 安全交給專業,少扛 PCI 負擔
geo 搜尋 第三方 → PostGIS,必要才 Redis GEO 一步到位的炫技 寫吞吐低,避免過早上重武器
收尾話術

「整題的單一錨點是 邏輯可用性取代實體鎖:訂房既要 consistency(不 double booking)又不能長持鎖卡死 DB,所以我把『可用』算成 available OR 過期的 reserved,用一筆原子 conditional write 搶位,cron 只負責事後清理、延遲了也不影響正確。其餘是兩條標準線——搜尋走 availability(inventory 預算化 + index/ES + cache)、讀擴展走 stateless + replica 撐旺季。geo / 付款都先用便宜方案,不過早上重武器。」引用既有決策用連結,不重新推導。

後續追問演練 (Interviewer Follow-ups — "What if…")

為什麼搜尋偏 availability,訂房偏 consistency?同一系統能這樣嗎?

「邏輯可用性 > 實體狀態」具體指什麼?

搜尋的 ~400K QPS 怎麼估出來的?

流量再放大 10x(旺季尖峰 ~4M QPS),架構哪裡先撐不住?怎麼補?

某熱門 home 在熱門日期被狂搶(hot row),原子 conditional write 全壓在同一列怎麼辦?

cron job 掛了一小時,會發生什麼?系統還正確嗎?

使用者付款失敗或中途關掉頁面,inventory 怎麼回收?

為什麼不用分散式鎖(如 Redis/Zookeeper 鎖)來搶位,而是用 DB transaction?

inventory 預先攤成「每房每天一列」值得嗎?有沒有更省的做法?

Elasticsearch 與 Postgres 不一致(CDC 落後)時,使用者會看到什麼?可接受嗎?

主動回想:白板重建 (Whiteboard from Scratch)

不看上文,照下面提示把整個設計重建一次。每格都該能 30 秒內口述。

常見陷阱 (Exam Traps for This Problem)