【從一台 Server 到分散式架構】第 27 篇:用同樣的思維看 Uber——即時位置與派車系統

跟 YouTube 不同,YouTube 的挑戰主要是「讀多寫少」——觀看遠多於上傳。

Uber 的挑戰是:讀多、也寫多、而且高度即時

  • 每台上路的司機,每 4 秒更新一次位置
  • 全球幾百萬台司機,每秒幾十萬筆位置更新
  • 乘客叫車後,要在幾秒內找到合適的司機並發送派單
  • 派單要剛好派給一位司機,不能重複,不能漏

把 Uber 拆成幾個子問題

  1. 位置更新:司機 / 乘客的位置怎麼存、怎麼查
  2. 附近司機搜尋:給定一個位置,如何快速找到方圓 N 公里的司機
  3. 配對與派單:找到候選司機後,如何決定派給誰、如何確保只派給一人
  4. 行程管理:行程開始、進行中、結束的狀態機
  5. 即時通知:乘客和司機的即時更新(位置、狀態)

問題一:位置更新——高寫入頻率

全球幾百萬台司機,每 4 秒更新一次位置,是每秒幾十萬筆的寫入。

普通的關聯式資料庫扛不住這種頻率。

解法:專門的位置服務 + 記憶體儲存

  • Redis 存每台司機的「當前位置」(不需要歷史,只需要最新位置)
  • Redis 的寫入速度足以應付這個頻率
  • 同時,位置更新也可以先寫進 Message Queue,再由 Worker 處理——削峰填谷(第 08 篇)

對於位置的歷史記錄(例如行程軌跡),再另外存到適合寫多的 DB(Cassandra / InfluxDB)。


問題二:附近司機搜尋——地理空間索引

「找出方圓 5 公里內的空閒司機」,如果用普通的資料庫查詢:

SELECT * FROM drivers
WHERE lat BETWEEN ? AND ? AND lng BETWEEN ? AND ?
AND status = 'available'

這樣的範圍查詢,在幾百萬筆資料裡非常慢,而且「方形範圍」不是真正的圓形距離。

解法:Geohash 或 S2 地理空間索引

Geohash 把地球表面切成一格一格,每個格子有一個字串編碼。相鄰的格子,字串前綴也相同(大致):

台北市中心 → Geohash: "wqqm"
附近某點  → Geohash: "wqqk"  (前綴 wqq 相同)

把司機依 Geohash 分組存在 Redis 裡,搜尋附近司機就變成「找同一格或相鄰格的所有司機」,速度快很多。

Redis 本身也支援 GEO 指令(GEOADDGEORADIUS),可以直接做到「找半徑 N 公里內的所有司機」,是很多簡易版本的選擇。


問題三:配對與派單——不能重複派

找到幾個候選司機後,要選一個發送派單。

問題:如果同時有兩個乘客叫車,找到的候選司機有重疊,可能兩個乘客都把 A 司機選為最佳選項,同時向 A 發派單——A 只能接一個,另一個就白等了。

解法:分散式鎖(第 18 篇)

派單前,對「司機 ID」加鎖:

嘗試對 driver:A 加鎖
→ 成功 → 向 A 發派單,等 A 確認後釋放鎖(或拒絕後釋放)
→ 失敗 → A 已被鎖定,選下一個候選司機

這樣就算多個派單系統同時競爭,也只有一個能成功向 A 發出請求。


問題四:乘客和司機的即時通訊——WebSocket

行程進行中,乘客要看到司機的位置在移動;司機要收到新的派單通知。

這就是第 17 篇的 WebSocket + Pub/Sub——完全一樣的架構:

  • 乘客 App 和司機 App 各自建立 WebSocket 連線
  • 位置更新透過 Pub/Sub 廣播給相關的 WebSocket Server
  • WebSocket Server 推送給對應的 App

Uber 的高層架構


Uber 的核心取捨

挑戰解法取捨
司機位置高頻寫入Redis 存當前位置,MQ 做緩衝位置可接受幾秒的延遲,不需要強一致
附近司機查詢Geohash / Redis GEO速度換精確度:邊界情況可能找不到幾百公尺外的司機
派單不重複分散式鎖鎖的粒度影響效率;司機拒絕後要快速釋放
即時推送WebSocket + Pub/Sub長連線資源消耗,離線要有補推機制

小結

Uber 看起來複雜,但拆開來都是熟悉的工具組合:

  • 高頻位置寫入 → Redis + Message Queue(第 05、08 篇)
  • 地理空間搜尋 → Geohash / Redis GEO(索引的一種)
  • 派單不重複 → 分散式鎖(第 18 篇)
  • 即時通訊 → WebSocket + Pub/Sub(第 17 篇)

下一篇,我們換個場景:Twitter / 社群動態系統——讀多寫也多,加上時序和關注關係,又是一套不同的取捨。

相關推薦

2026-04-07
【從一台 Server 到分散式架構】第 31 篇:從面試題反推架構——用本系列思維拆解系統設計考題
「設計一個 URL 短網址服務」「設計一個訊息系統」——系統設計面試問的不是背答案,而是思考方式。這篇整理了一套可以重複使用的面試框架,並用兩道例題示範如何從需求出發,一步步推導出架構、說清楚取捨。
2026-04-06
【從一台 Server 到分散式架構】第 30 篇:限流、排隊與降級實戰——開賣與直播場景
平常流量是 1000 QPS,但「開賣」的那一刻,瞬間湧入 50 萬個請求——系統要怎麼活下來?這篇用課程平台的限量課程開賣和直播開播場景,把第 13 篇學到的限流、降級、熔斷,落地到一個真實的高流量設計,走過每個防護層是怎麼工作的。
2026-04-05
【從一台 Server 到分散式架構】第 29 篇:用同樣的思維看 ChatGPT——AI 聊天系統架構
ChatGPT 看起來像一個聊天視窗,背後卻有幾個特殊的設計挑戰:回應是串流的、推理非常耗資源、每輪對話要記住上下文、系統要支援幾千萬用戶同時使用。這篇用熟悉的架構思維,拆解 AI 聊天系統的關鍵設計。
2026-04-04
【從一台 Server 到分散式架構】第 28 篇:用同樣的思維看 Twitter——社群動態與時序設計
Twitter 的核心功能看起來很簡單:發推文、看動態、按讚留言。但「動態牆」背後藏著一個棘手的設計問題:你追蹤 200 個人,每個人都可能隨時發文——你打開 App 時,那條時序動態要怎麼快速組出來?這篇來看社群動態系統的兩種策略:推(Fan-out on Write)與拉(Fan-out on Read)。