前言
你是否想過,當你打開 Yelp 或 Google 地圖,系統是如何在不到一秒內,從全台甚至全球數百萬家餐廳中,精準篩選出「離你最近的 10 家火鍋店」?
這類系統的核心挑戰在於空間索引(Spatial Indexing)。
這篇文章透過五維度分析法,拆解 LBS(Location-Based Service,適地性服務)系統背後的底層架構與技術取捨。
這篇你可以帶走什麼
- LBS 系統在資料量與流量上的真實壓力點
- 為什麼傳統 SQL 與複合索引不適合做高效附近搜尋
- QuadTree / Geohash 如何把二維搜尋降維成可擴展結構
- 何時該放棄強一致性,改採最終一致性與定期重建
維度一、可直接觀察的事實:系統規模與流量特徵
先從客觀量級出發。
假設系統有:
- 5 億日活躍用戶(DAU)
- 2 億家商家
若每筆商家資料(經緯度、地址、簡短描述等)約 10KB,總資料量約:
- 2 億 x 10KB = 約 2TB
2TB 對現代儲存體系不是天文數字,真正困難在查詢模型。
再看流量特徵,會出現極端不對稱:
- 讀取(Search)極高:使用者不斷拖曳地圖、搜尋附近店家,尖峰搜尋 QPS 可達 100,000
- 寫入(Update)極低:商家搬家、更新資訊頻率很低,尖峰寫入 QPS 甚至不到 300
這代表你要優先優化讀取路徑,而不是寫入路徑。
二維空間搜尋困境:
- 如果用傳統 SQL 語法(例如計算兩點距離 X² + Y² < 半徑),資料庫必須「全表掃描(Full Table Scan)」來進行浮點數運算,效能極差。
- 即使建立傳統的「複合索引(Composite Index)」,也無法同時支援經度與緯度的「雙向範圍查詢」。
重點:LBS 的瓶頸不在資料量本身,而在二維空間查詢模型與極端讀多寫少的流量型態。
維度二、條件檢查:完美解法與它的邊界
一般情況下的最佳解
業界標準做法是降維打擊:使用 QuadTree 或 Geohash。
以 QuadTree 為例,做法是把地圖持續切成四個象限,直到每個網格內商家數低於 100 家。
為了控制記憶體,可在 QuadTree 節點中只放精簡資訊:
- 店家 ID
- 經緯度
- 50 字短描述
在這種精簡策略下,2 億商家的索引樹約 10GB,可放入單台伺服器 RAM,查詢延遲可壓到毫秒級。
邊界條件(什麼時候失效)
當寫入變得頻繁且要求即時時,這套架構會變痛苦:
- 因為 QuadTree 存在記憶體中,如果某個網格內的店家突然超過 100 家,該節點必須「分裂」成 4 個子節點,這牽涉到複雜的樹狀結構重組與鎖(Lock)的併發問題。
也就是說,QuadTree 很適合「讀多寫少」,不適合「高頻即時寫入」。
重點:QuadTree 的強項是快速查詢,不是高頻更新。
維度三、反證檢查:跳脫「強一致性」的直覺慣性
直覺上,我們常認為店家地址一改,地圖就要立刻更新。
但反直覺地看,這個需求在多數 LBS 場景不一定成立:
- 店家搬家資訊晚 1 小時甚至晚 1 天顯示,對一般使用者體驗通常影響有限
因此可改採最終一致性(Eventual Consistency):
- 把線上 QuadTree 視為唯讀(Read-only)
- 透過排程(Cron Job)每小時或每天重建一次整棵索引樹
- 在新樹建好後平滑替換舊樹
若整棵樹約 10GB,重建通常只需幾分鐘,卻能避開複雜即時更新與死鎖(Deadlock)風險。
維度四、不確定性與假設分析:假設與地理隔離
要先分清楚觀察事實與經驗推論:
- 觀察事實:全域 QuadTree 約 10GB
- 經驗推論:大多使用者搜尋是同城或同區域,跨洲附近搜尋很少見
核心假設是:區域流量可以有效隔離。
因此你不需要每個機房都載入整顆全球樹,而是可按區域部署:
- 美東 / 美西
- 東京 / 台灣
好處是:
- 降低單區域負載
- 縮小 Blast Radius(爆炸半徑,指系統故障時的影響範圍)
維度五、最終結論與行動建議
系統藍圖可以分三層:
- 底層資料庫(RDBMS 或 NoSQL):存放約 2TB 商家完整資訊,負責低頻寫入與明細讀取
- 應用層建構一台(或一組)搭載 QuadTree 的記憶體伺服器,專門處理 100k QPS 的附近地點搜尋。
- 設計一個背景排程任務,定期(如每小時)重構 QuadTree 並發布到線上服務。
破關路線圖(信心度 95%)
- 先把附近搜尋主路徑切到空間索引(QuadTree / Geohash),避免 SQL 全掃
- 索引節點只保留必要欄位,優先確保 RAM 可承載
- 將線上索引改為唯讀,改用排程重建與平滑替換
- 依地理區域拆分部署,降低跨區延遲與故障擴散
引入空間索引與最終一致性,是 LBS 系統在面試與業界都非常穩健的基本盤。
脆弱點提示
若你把這套架構直接拿去做外送員即時定位追蹤(如 Uber Eats / foodpanda),它會很快崩潰。
原因是那類場景屬於:
- 寫入頻率極高(座標每秒變動)
- 對位置一致性要求更高
此時「定期重建 QuadTree」不再適用,通常要改成即時資料流架構,例如 Redis GEO + 串流處理。
一句話總結
LBS 附近搜尋的勝負手在空間索引與一致性策略:用 QuadTree 扛查詢、用最終一致性降複雜度,再用區域化部署控制風險半徑。