系統設計面試:設計Uber
「設計Uber」是最常見的系統設計題之一,因為它同時涉及地理空間索引、即時位置串流、配對,以及高寫入吞吐量。面試官並不是要聽真正的Uber架構——他們想看你如何界定問題範圍、做出有依據的選擇,並大聲推理規模問題。
按照有條理的順序進行:先釐清需求,再估算規模,勾勒核心實體與API,設計資料與配對路徑,最後討論瓶頸與取捨。以下的解析遵循這個順序,讓你練習的是結構本身,而不只是答案。
1. 釐清需求
在設計之前,先把功能性需求與非功能性需求區分開來,並固定範圍。乘客發出乘車請求,即時看到司機接近;司機上線並接收附近的請求;系統將兩者配對,並追蹤行程直到完成。
- 功能性:發出乘車請求、與附近司機配對、即時追蹤行程、計費、行程紀錄。
- 非功能性:低延遲配對(數秒)、高可用性、即時位置更新、一致性——一位司機只會被分配給恰好一位乘客。
- 範圍之外(要明確說出來):付款處理的內部細節、評分、動態加價的機器學習——提到即可,然後延後處理。
2. 估算規模
粗略的數字能為之後的選擇提供依據。假設每日活躍司機約1000萬,每4秒傳送一次位置回報——這代表每秒約250萬次位置資料的寫入,這是一個以寫入為主的負載,立刻排除了把簡單關聯式資料表當作熱路徑的方案。
3. 核心元件與API
勾勒出服務與請求流程。讓配對路徑獨立於持久化的行程儲存,這樣各自可以依照自身特性擴展。
- 位置服務:接收司機的位置回報,將目前位置保存在記憶體內的地理空間索引中。
- 配對服務:收到乘車請求時,查詢附近在線的司機並分配一位。
- 行程服務:持久化記錄行程的生命週期(已請求 → 已配對 → 進行中 → 已完成)。
- 通知/串流:將司機的即時位置推送給乘客(WebSocket/長輪詢)。
4. 地理空間配對——問題的核心
這是面試官最想聽到的部分。儲存原始經緯度並逐一掃描是O(n)的;你需要一個空間索引。說明四分樹(quadtree)或地理雜湊(geohashing):把地圖劃分成網格,讓「尋找這個點附近的司機」變成查詢少數幾個網格,而不是全量掃描。
地理雜湊把一個區域編碼成一個短字串,共享前綴代表空間上的相鄰,讓「附近」變成一個前綴查詢。司機目前所在的網格保存在記憶體儲存(例如Redis)中,以實現快速讀取與頻繁更新;網格的粒度是精確度與查詢扇出之間可調整的取捨。
5. 可擴展性與瓶頸
指出壓力點以及你會如何緩解它們。每秒250萬次位置寫入的資料流是最主要的壓力點。
- 依地理區域對位置索引進行分片——同一城市的司機與乘客由同一分片處理,讓配對保持在本地。
- 用記憶體儲存與非同步持久化寫入來吸收寫入高峰;並不需要每一次回報都寫入持久化資料庫。
- 用原子化分配(鎖定或條件更新)來保證一位司機只對應一位乘客,避免重複預訂。
- 在位置資料接收與下游消費者之間使用訊息佇列(Kafka)來解耦突發流量。
6. 需要說出來的取捨
最後點出面試官看重的那些張力:強一致性與最終一致性(司機分配必須是強一致的,位置顯示可以是最終一致的)、記憶體速度與持久性、網格大小的精確度與查詢扇出。明確說出這些取捨,才是及格答案與出色答案的分水嶺。
藉助即時輔助練習Uber的系統設計
Natively的系統設計面試助手能在實際面試中幫你保持結構清晰——即時建議接下來該涵蓋的面向,並呈現取捨,一切都在你自己的裝置上完成。
