查询"附近的人"没那么难:PostGIS地理查询实操

举报
茉莉风铃 发表于 2026/09/14 09:56:48 2026/09/14
【摘要】 用 PostGIS 的 ST_DWithin 和 GIST 索引,用一条 SQL 圈出指定坐标周边指定范围内的商铺,并说明空间索引的注意点。

“帮我找附近 1 公里的奶茶店”——这种需求几乎每个带位置的应用都要做。很多人一上来就用经纬度算距离再 ORDER BY,数据一大就慢得要命。其实 PostgreSQL 装个 PostGIS 扩展,地理查询就是一行空间函数的活儿。我把这套用在了商铺推荐里,记录一下。

image.png

一、空间数据和普通字段不一样

经纬度看着是两个数字,但直接拿来做距离计算,得考虑地球是圆的、度数不等于米。PostGIS 提供了 geometry 类型和一堆空间函数,还支持 GIST 空间索引,让"范围内筛选"走索引而不是全表扫。这才是它快的根本原因。

二、建表与索引

给商铺表加一个 geom 字段存坐标点,并建 GIST 索引:

CREATE EXTENSION IF NOT EXISTS postgis;

CREATE TABLE shops (
  id    serial PRIMARY KEY,
  name  text,
  geom  geometry(Point, 4326)   -- 4326 = WGS84 经纬度坐标系
);

CREATE INDEX idx_shops_geom ON shops USING GIST (geom);

4326 是常用的经纬度坐标系 SRID,前后端拿到的 GPS 坐标基本都是这个,别乱填别的。

三、一条 SQL 圈出周边

以用户坐标 (116.40, 39.90) 为中心,找 0.01 度(约 1 公里)内的店,按距离排序:

SELECT name,
       ST_Distance(
         geom,
         ST_SetSRID(ST_MakePoint(116.40, 39.90), 4326)
       ) AS dist
FROM shops
WHERE ST_DWithin(
        geom,
        ST_SetSRID(ST_MakePoint(116.40, 39.90), 4326),
        0.01
      )
ORDER BY dist
LIMIT 20;

ST_DWithin 做的是"是否在给定的距离内",配合 GIST 索引能快速圈定候选,比先算全部距离再过滤高效得多。

四、性能上的几个细节

做法 是否走索引 备注
ST_DWithin + GIST 索引 推荐,范围筛选走索引
ST_Distance(...) < x 否(通常) 先计算再过滤,大表慢
存经纬度两列自己算 Haversine 麻烦且易错

踩过的最大的坑:用了 ST_Distance < 阈值 却没走索引,查询慢十倍。换成 ST_DWithin 后立刻起飞。另外距离单位要看 SRID,4326 下 ST_Distance 返回的是度数,要米的话用 ST_Distance(..., true) 或统一投影到 3857。

总结一下

"附近的人/店"本质是空间范围查询,PostGIS 把这件事做得既标准又快。记住两句话就够用:坐标存 geometry 字段、范围筛选用 ST_DWithin 并配 GIST 索引。剩下的距离排序、分页都是 SQL 基本功。主流云数据库大多提供 PostGIS 兼容能力,这套写法基本能直接迁移。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。