設計附近地點服務 (Proximity Service)

Hard★★★★Draft

FromSystem Design Interview

Concepts practiced🗄️ SQL vs NoSQL 選型⚡ 快取策略 (Caching Strategies)📈 負載均衡 (Load Balancing)

Convention: read the requirements & constraints first, sketch your own design, then expand the reference to compare.

📚 From the Books

設計附近地點服務 (Proximity Service)ProblemHardGeo & Location

給定使用者位置與半徑,回傳附近的商家(餐廳、飯店、加油站…)。難點在於把二維的經緯度變成能被索引、能快速搜尋的資料。

📋 Requirements

🧠 Core Ideas

TIP

核心是「把二維經緯度映射到一維、切成小格建索引」,讓半徑搜尋不必掃全表;再靠讀多寫少的特性重讀取路徑(replica + 快取)。

讀取量極高(搜尋附近 + 看詳情),寫入量低(增修商家不頻繁)。這種讀多寫少的負載,用關聯式資料庫(MySQL)是合適選擇,主要兩張表:business 表與 geospatial 索引表。

⚖️ Reference Design

高階架構
  • 負載平衡器:單一 DNS 進入點,依 URL 路徑把請求路由到對應服務。
  • LBS(location-based service):系統核心,只讀、無狀態、QPS 高 → 容易水平擴展。
  • 商家服務:處理增修商家(寫,QPS 低)與看詳情(讀,尖峰高)。
  • 資料庫叢集:主從設定,主庫寫、多 replica 讀;因商家不需即時更新,複寫延遲造成的不一致可接受。
地理空間索引方案比較
  • 二維搜尋:直接用經緯度範圍畫方框查詢。索引只能加速單一維度,兩個 dataset 還要 intersect,效率差。
  • 均勻切格:世界切成等大方格。問題是商家分佈不均(都市 vs 沙漠),且找相鄰格麻煩。
  • Geohash:遞迴把世界切成四象限、逐 bit 縮小,把二維座標編成一維字串。共用前綴越長越接近;用長度 4~6 對應不同半徑。邊界案例需一併查相鄰格。實作簡單、更新容易、支援半徑查詢,但格子大小固定、無法隨密度調整。
  • Quadtree:記憶體內樹狀結構,遞迴切成四象限直到每格商家數不超過門檻(如 100)。能隨人口密度調整格子、支援回傳 k 個最近商家;但需建樹、更新較複雜、伺服器啟動要花時間建樹。
  • Google S2:記憶體內方案,用 Hilbert curve 把球面映射到一維,擅長 geofencing 與彈性的 region cover。

面試建議選 geohash 或 quadtree(S2 較難口頭講清楚)。

資料表擴展
  • business 表:資料大,依 business_id 分片,負載均勻、易維護。
  • geospatial 索引表:整份資料其實不大(quadtree 約 1.71GB,geohash 類似),能裝進單機工作集。瓶頸是讀取量而非容量,因此優先加 read replica分擔讀取,而非直接分片(分片邏輯要進應用層,複雜且無必要)。
  • geohash 索引表用 (geohash, business_id) 複合主鍵、一列一商家,增刪都無需鎖。
快取
  • 先問「真的需要嗎」:資料集小、可全進工作集,查詢幾乎跟記憶體一樣快;讀取瓶頸也能靠 replica 解。要導入前需 benchmark 與成本分析。
  • 快取鍵不用原始座標(手機座標不精確、會微幅漂移);用 geohash,位置小變動仍對應同一格。
  • 快取兩類資料(cache-aside):geohash → 格內 business ID 清單business_id → business 物件。以三種精度(geohash_4/5/6)預先計算,全球約需 5GB。
  • 因「隔天生效」的約定,快取可用每晚工作更新。

🔑 Takeaways

✍️ My Notes

No notes yet — jot your takeaways or Q&A here.

📖 Further Reading