本节摘要:空间查询要扫全表很慢,靠空间索引加速。本节讲 R 树、网格索引、PostGIS 的 GiST 索引——让空间查询快起来。
阅读完本节,你应当能够:
"找 1km 内餐厅"不索引要逐个算距离,表大就慢。空间索引按空间位置组织数据,快速定位候选要素,再精确计算,避免全表扫描。
R 树是空间索引的主流数据结构,用最小外包矩形(MBR)递归组织空间对象:

R 树把空间对象按 MBR 分组,查询时先比 MBR,不相交的整组跳过,快速缩小范围。
PostGIS 用 GiST 索引实现 R 树:
-- 建空间索引 CREATE INDEX idx_restaurants_geom ON restaurants USING GIST (geom); -- 建完要分析统计 VACUUM ANALYZE restaurants;
建完空间查询自动用索引,ST_DWithin、ST_Intersects 等都能加速。
EXPLAIN SELECT * FROM restaurants WHERE ST_DWithin(geom, my_point, 1000); -- 看 Query Plan 是否用了 GIST 索引扫描
⚠️ 常见坑:建了空间索引查询还是慢——可能没 VACUUM ANALYZE 统计过期,或查询没用上索引(EXPLAIN 看计划)。
💡 关键直觉:空间索引用 R 树按 MBR 组织,查询先比 MBR 快速过滤候选再精确算。PostGIS 用 GiST 建索引,建完 VACUUM ANALYZE,EXPLAIN 验证生效。
R 树为什么快,可以用一个想象场景说明:把一张城市地图按行政区划分成若干大区域,每个大区域再分小格。查"某点 1 公里内的餐厅"时,先判断这个点落在哪个大区域,只翻那个区域里的餐厅记录,而不是把全城餐厅都翻一遍。R 树做的事情就是"把空间对象按位置组织成有层次的外包矩形树",每个父节点只存孩子的最小外包矩形,查询时从根往下剪枝。
它的查找过程分两阶段:
所以空间索引的效果上限,取决于查询范围相对数据分布是否"挑得出":查询范围覆盖整张表时,索引几乎帮不上忙,这很正常。
| 数据库 | 索引类型 | 说明 |
|---|---|---|
| PostGIS | GiST | 最常用,基于 R 树 |
| MySQL InnoDB | R 树 | 5.7+ 空间索引 |
| SQL Server | 网格 + 四叉树混合 | 自动选择 |
| Oracle Spatial | R 树 | SDO_INDEX |
| MongoDB | 2dsphere | GeoJSON 地理索引 |
| Elasticsearch | geo_shape | 空间检索 |
实现虽有差异,使用逻辑一致:对几何列建索引,查询优化器自动选择。在 PostGIS 里你也可以用 BRIN 索引(对顺序扫描友好的块级索引),适合只追加、很少更新的海量时序空间表。
实际项目中索引不是"建一次就完事",维护要点:
VACUUM ANALYZE 表名。EXPLAIN SELECT ... 看执行计划里有没有 Index Scan using ..._geom,没有就检查条件写法(比如对 geom 列包了函数导致索引失效)。-- 查看表上已有的索引 SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'parcels'; -- 删除后重建(适合大批量导入前) DROP INDEX idx_parcels_geom; -- 导入完成后重建 CREATE INDEX idx_parcels_geom ON parcels USING GIST(geom); VACUUM ANALYZE parcels;
USING GIST 建索引,建完 VACUUM ANALYZE 更新统计。EXPLAIN 看是否用 GIST 索引扫描。下一节讲常用 GIS 数据格式。