為連鎖飯店設計訂房系統。TPS 不高,難點在併發訂同一房型的競態與避免重複訂房——同一套設計也適用於 Airbnb、航班與電影票訂位。
📋 Requirements
- 功能:顯示飯店/房間頁、預訂房間、管理後台(新增/移除/更新飯店房間)、支援超賣。
- 規模:5,000 間飯店、100 萬間客房;每日訂房約 24 萬筆 ≈ 3 TPS——重點不在吞吐量。
- 非功能:支援高併發(旺季熱門房間大量客戶搶訂)、中等延遲(處理訂房花幾秒可接受)。
- 訂房時全額付款、可取消;房價每日動態變化;允許 10% 超賣(預期部分客戶取消)。
🧠 Core Ideas
TIP
使用者訂的是房型 (room type),不是特定房間——房號入住時才給。庫存以 room_type_inventory 表按「每個日期一列」建模,欄位 total_inventory 與 total_reserved,複合主鍵 (hotel_id, room_type_id, date),未來 2 年的列由每日排程預先填入。
檢查可訂房:選出日期區間內的列,逐日驗證 (total_reserved + rooms) <= total_inventory;支援超賣只需改成 <= 110% * total_inventory。選關聯式資料庫:讀多寫少、業務關係穩定易建模,且 ACID 保證能避免負餘額、重複扣款、重複訂房。
⚖️ Reference Design
微服務與 API
- microservice 架構:CDN + API Gateway(限流/認證/路由)後接 hotel、rate、reservation、payment 等服務;跨服務用 gRPC。
- 建立訂房
POST /v1/reservations帶roomTypeID、roomCount與reservationID。 reservationID同時作為 idempotency key,避免同房型同日被重複下單。
併發:重複訂房與競態
- 同一使用者重複點擊:靠冪等 API——
reservation_id作 reservation 表主鍵,第二次提交撞唯一性限制即失敗。(客戶端灰化按鈕不可靠。) - 多人搶最後一間:非 serializable 隔離下,兩交易都讀到「還有 1 間」而雙雙成功→超賣。需鎖定機制。
- 悲觀鎖(
SELECT ... FOR UPDATE):序列化更新,但易 deadlock、不具擴展性——不建議。 - 樂觀鎖(version number):不鎖庫,衝突才重試;訂房 QPS 通常不高,是好選項。
- 資料庫限制(
CHECK(total_inventory - total_reserved >= 0)):實作最簡單,低競爭下運作良好,也是好選項。
擴展與快取
- 服務無狀態可水平擴展;資料庫按
hotel_id分片(多數查詢先選飯店)。 - 只有當前與未來庫存有意義→用 Redis(TTL + LRU)做 inventory cache,key 為
hotelID_roomTypeID_{date}。 - 讀路徑走快取,但資料庫才是真實來源,訂房時仍在 DB 端重新驗證庫存;DB→cache 用非同步更新或 CDC(如 Debezium)傳播。
🔑 Takeaways
- 訂的是房型不是房間——庫存表按日期建模,是本題最關鍵的資料模型修正。
- 重複訂房用冪等鍵 + 唯一性限制擋;搶房競態用樂觀鎖或 DB 限制(別用悲觀鎖)。
- 快取與 DB 不一致其實無所謂,因為 DB 做最終驗證;快取只為減負載、加速讀取。
- 訂房與庫存放同一關聯式資料庫(混合方法),用 ACID 省掉跨服務分散式交易(2PC / Saga)的複雜度——作者判斷純 microservice 拆庫不值得。
No notes yet — jot your takeaways or Q&A here.