開發基於地理位置的應用(LBS)時,將距離計算下推至資料庫層是效能最優解:資料已在伺服器端、C/C++ 實現的擴充套件極快、且可利用 GIST 索引加速空間查詢。本文介紹 PostgreSQL 輕量級組合——cube 與 earthdistance,無需龐大的 PostGIS 即可快速構建「附近的人/事」功能。
| 方案 | 特點 | 適用場景 |
|---|---|---|
| PostGIS | 功能完整(投影、拓撲、光柵)、重量級、需額外維護 | 專業 GIS、複雜空間分析 |
| Cube + Earthdistance | 輕量、內建擴充套件、僅支援球面大圓距離、索引簡單 | LBS 應用(附近餐廳、打卡點、物流調度) |
若你只需「兩點距離」、「半徑篩選」、「最近 N 筆」,Cube + Earthdistance 是最佳 CP 值之選。
-- 1. 建立資料庫(略)
-- 2. 啟用擴充套件(需 superuser 權限)
CREATE EXTENSION cube;
CREATE EXTENSION earthdistance;
-- 兩指令共建立約 40 個函數與操作子
CREATE TABLE events (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
lat DOUBLE PRECISION, -- 緯度
lng DOUBLE PRECISION -- 經度
);
使用 DOUBLE PRECISION 儲存經緯度,兼容 Node.js 等應用層無需自訂型別轉換。
| 函數 | 回傳 | 說明 |
|---|---|---|
ll_to_earth(lat, lng) | earth | 經緯度轉球面座標點 |
earth_distance(pt1, pt2) | float8 (公尺) | 兩點大圓距離 |
earth_box(pt, radius_m) | cube | 以點為中心、半徑為邊長的立方體(供 GIST 索引掃描) |
gc_to_sec / sec_to_gc | float8 | 大圓距離與割線距離互轉 |
大圓距離:球面上兩點最短路徑(黎曼圓弧長),符合真實導航距離。
SELECT
id,
name,
earth_distance(
ll_to_earth(:my_lat, :my_lng),
ll_to_earth(lat, lng)
) AS dist_m
FROM events
ORDER BY dist_m ASC
LIMIT 10;
回傳最近 10 筆事件,距離單位為公尺。
-- 建立 GIST 索引(關鍵加速步驟)
CREATE INDEX idx_events_geo ON events USING GIST (ll_to_earth(lat, lng));
-- 查詢:利用 earth_box 先粗略過濾,再精確計算
SELECT id, name
FROM events
WHERE earth_box(ll_to_earth(:my_lat, :my_lng), 5000) @> ll_to_earth(lat, lng)
AND earth_distance(ll_to_earth(:my_lat, :my_lng), ll_to_earth(lat, lng)) <= 5000;
@> 為 Cube 的「包含」操作子,配合 GIST 索引極速篩選候選集。earth_distance 精確剔除立方體角落誤差。CREATE INDEX ... USING GIST (ll_to_earth(lat, lng));DOUBLE PRECISION,避免 NUMERIC 運算較慢。CLUSTER 依索引重排資料頁,提升序列掃描效率。僅需兩行 CREATE EXTENSION、一個 GIST 索引、標準 DOUBLE PRECISION 欄位,即可在 PostgreSQL 內建強大的 LBS 查詢能力:
下次專案需要「附近的咖啡廳」、「派單給最近騎手」時,請優先評估 Cube + Earthdistance,輕量上手、效能驚人。