PostGIS 空间索引为什么没生效?EXPLAIN、ST_Intersects 与范围过滤排查
问题场景:建了 GiST 索引,查询为什么还是慢
PostGIS 空间查询慢时,很多人会先确认是否创建了 GiST 索引。索引确实重要,但它不是魔法开关。查询写法不合适、在字段上临时做 ST_Transform、统计信息过旧、返回数据比例太高,都可能让优化器放弃索引或索引收益很低。
排查空间索引问题,不能只看“有没有索引”,而要看执行计划。EXPLAIN ANALYZE 是判断数据库实际怎么执行查询的关键工具。
先确认索引和统计信息
CREATE INDEX idx_parcels_geom
ON parcels
USING GIST (geom);
ANALYZE parcels;
创建索引后应更新统计信息。对于频繁导入、删除或批量更新的数据表,统计信息过旧会影响优化器判断。
用 EXPLAIN 看执行计划
EXPLAIN ANALYZE
SELECT p.*
FROM parcels p
JOIN roads r
ON ST_Intersects(p.geom, r.geom);
如果计划中出现 Index Scan 或 Bitmap Index Scan,说明索引参与了查询。如果大量 Seq Scan 且表很大,就需要检查查询条件和数据分布。
避免在索引字段上包函数
常见错误是这样写:
ST_Intersects(ST_Transform(p.geom, 3857), r.geom)
对索引字段临时转换,会让数据库难以直接使用原字段上的索引。更稳的做法是让数据提前统一 CRS,或把查询参数转换到表的 CRS。高频接口可以增加投影后的几何字段并单独建索引。
| 写法 | 索引友好程度 | 建议 |
|---|---|---|
| ST_Intersects(geom, param_geom) | 高 | 推荐 |
| ST_Transform(geom) | 低 | 避免用于高频查询 |
| 无范围条件的大结果查询 | 不稳定 | 增加 bbox 或分页 |
空间谓词与范围过滤
ST_Intersects 等常见谓词通常会利用边界框过滤。但在复杂 SQL 中,可以显式加入 && 作为候选范围过滤,再做精确判断。这样执行计划更清晰,也方便调优。
项目避坑:索引不等于少返回数据
如果查询条件会命中表中大部分要素,走索引未必更快。索引适合快速缩小候选集,不适合替代合理的数据范围和业务过滤条件。
WebGIS 接口应尽量按地图视窗、图层级别、行政区或业务条件过滤,不要一次返回全城所有几何。
FAQ
为什么小表不走空间索引?
表很小时,全表扫描可能比索引更便宜,这是正常优化器选择。
ST_Intersects 一定会用索引吗?
不一定。是否使用索引取决于执行计划、数据量、统计信息和查询写法。
空间索引能加速面积计算吗?
索引主要加速空间过滤。对已选中的大量复杂几何做面积计算,仍然需要计算成本。
总结
PostGIS 性能优化要从执行计划出发。索引、SQL 写法、CRS 统一、统计信息和返回数据量共同决定速度。先用 EXPLAIN 找瓶颈,再改查询,才是真正有效的优化。