返回首页

Redis数据结构3.3: GEO 地理位置

适用版本:Redis 7.2.x

一、什么是 GEO

GEO 是 Redis 提供的一组地理位置能力,用来存储和计算 经纬度坐标,并支持常见的地理查询需求,例如:

  • 附近的人
  • 附近的门店
  • 两地之间的距离
  • 某个区域内有哪些位置点

从使用方式上看,GEO 像是一种独立能力;但从底层实现来看,它其实是 基于 Sorted Set(有序集合)实现的地理位置索引方案

这意味着:

  • 每个成员对应一个地理位置
  • Redis 会把经纬度编码成可排序的值
  • 再借助有序集合完成范围检索

因此,GEO 适合做轻量级、实时性强的地理检索场景,而不是复杂 GIS 系统的替代品。


二、GEO 的核心原理

1. 经纬度不是直接拿来排序的

如果只把经度、纬度原样存进去,是很难直接做“附近搜索”的。因为“空间上的近”不等于“数值上的简单区间近”。

Redis 的 GEO 方案会先对经纬度进行编码,把二维坐标映射为一个可排序的整数值,再利用 Sorted Set 的 score 或排序能力来组织数据。

2. 底层依赖空间编码思想

Redis GEO 的核心思想可以理解为:

  • 把地球上的位置切成网格
  • 相近位置编码后也尽量接近
  • 通过范围扫描找到候选点
  • 再结合真实距离做精确过滤和排序

因此它兼顾了:

  • 写入效率
  • 附近检索效率
  • 距离计算能力

3. 本质仍是“点数据检索”

GEO 特别适合处理“点”的位置问题,比如用户、骑手、门店、仓库,而不擅长:

  • 多边形地理围栏
  • 复杂路线规划
  • 行政区边界分析
  • 专业地图渲染

也就是说,GEO 更像“坐标附近检索工具”,而不是完整地图系统。


三、GEO 适用场景

1. 附近的人

社交类应用中常见需求:

  • 显示我附近 5 公里内的用户
  • 按距离排序展示附近的人

2. 附近门店/POI 检索

例如:

  • 查找 3 公里内的便利店
  • 查询当前定位周边的服务网点
  • 找到最近的仓库或站点

3. 距离计算

例如:

  • 计算用户与门店的距离
  • 计算骑手与商家的距离
  • 计算两个站点之间的直线距离

4. 配送或调度的候选筛选

在复杂调度系统中,GEO 常用于第一步粗筛:

  • 先找附近若干候选点
  • 再结合业务规则做二次筛选

四、常用命令与示例

Redis 7.2.x 中,GEO 常用命令主要包括 GEOADDGEODISTGEOSEARCHGEOHASHGEOPOS 等。

1. GEOADD:添加地理位置

先加入几个城市坐标:

GEOADD city:cn 116.405285 39.904989 beijing
GEOADD city:cn 121.472644 31.231706 shanghai
GEOADD city:cn 113.264385 23.129112 guangzhou
GEOADD city:cn 114.057868 22.543099 shenzhen

命令格式为:

GEOADD key longitude latitude member

注意顺序是:

  • 先经度 longitude
  • 再纬度 latitude

很多初学者最容易把这两个参数写反。

2. GEOPOS:查看成员坐标

GEOPOS city:cn beijing shanghai

可以返回成员当前保存的经纬度。

3. GEODIST:计算两点距离

GEODIST city:cn beijing shanghai km
GEODIST city:cn guangzhou shenzhen km

单位可以是:

  • m:米
  • km:千米
  • mi:英里
  • ft:英尺

这个命令适合快速计算两点直线距离。

4. GEOHASH:获取位置编码

GEOHASH city:cn beijing shanghai

返回位置编码字符串。平时业务中直接使用不算太多,但它有助于理解 GEO 的底层编码思想。

5. GEOSEARCH:按圆形范围搜索

查询北京附近 1500 公里内的城市:

GEOSEARCH city:cn FROMLONLAT 116.405285 39.904989 BYRADIUS 1500 km WITHDIST ASC COUNT 10

这个命令表示:

  • 从给定经纬度出发
  • 查找半径 1500 公里范围内的位置
  • 返回距离信息
  • 按距离升序排序
  • 最多返回 10 条

6. GEOSEARCH:以成员为中心搜索

beijing 为中心找附近城市:

GEOSEARCH city:cn FROMMEMBER beijing BYRADIUS 1500 km WITHDIST WITHCOORD ASC COUNT 10

这个写法在业务里很常见,因为很多时候你已经有一个中心点成员。

7. GEOSEARCHSTORE:把搜索结果保存下来

GEOSEARCHSTORE city:near:beijing city:cn FROMMEMBER beijing BYRADIUS 1500 km ASC COUNT 10

这样可以把结果存入新的 key,方便后续复用。


五、一个典型案例:附近门店查询

假设有一批门店坐标,需要实现“查看我附近 3 公里门店”的功能。

1. 设计 key

store:geo:beijing

2. 写入门店位置

GEOADD store:geo:beijing 116.397128 39.916527 store:001
GEOADD store:geo:beijing 116.410000 39.920000 store:002
GEOADD store:geo:beijing 116.430000 39.900000 store:003

3. 根据用户当前位置查询附近门店

GEOSEARCH store:geo:beijing FROMLONLAT 116.405285 39.904989 BYRADIUS 3 km WITHDIST WITHCOORD ASC COUNT 20

返回结果中可以带上:

  • 门店 ID
  • 与用户的距离
  • 门店坐标

然后在应用层再根据门店营业状态、库存、配送范围做二次过滤。


六、GEO 和 Sorted Set 的关系

理解 GEO 时,一个常见问题是:为什么说它基于 ZSet?

原因是:

  • Redis GEO 的底层存储就是有序集合
  • 地理位置会被转换成某种可排序的编码值
  • 成员名称仍然是 member

所以如果你对 Sorted Set 已经熟悉,就更容易理解 GEO 的能力边界。

但要注意:

  • 不建议直接手工修改其底层排序值
  • 应优先使用 GEO 专用命令维护数据

七、GEO 的优点

1. 使用简单

相比引入专业地图数据库或空间索引系统,Redis GEO 的上手门槛非常低,几个命令就能完成附近检索。

2. 适合高频实时查询

像“附近的人”“附近门店”这类高频查询,Redis 可以提供很好的实时响应能力。

3. 可与 Redis 其他结构组合使用

例如:

  • GEO 存坐标
  • Hash 存门店详情
  • Set 存门店标签
  • Stream 记录位置变更事件

组合起来可以搭出完整业务方案。


八、使用 GEO 时的注意事项

1. 参数顺序一定是“经度在前,纬度在后”

这是最常见的错误之一。

正确写法:

GEOADD key longitude latitude member

不是纬度在前。

2. 距离是球面距离的近似计算,不等于路线距离

GEODISTGEOSEARCH 给出的通常是地理直线距离或基于球面模型的距离近似值,不代表真实驾车距离、步行距离。

如果业务需要路线规划,应该接入地图服务,而不是只依赖 Redis GEO。

3. 更适合中小规模附近检索,不是专业 GIS 引擎

GEO 很适合“找附近点”,但不适合:

  • 多边形包含判断
  • 地理围栏复杂运算
  • 行政区边界分析
  • 复杂空间聚合

这类需求需要更专业的地理空间系统。

4. 数据更新频繁时要考虑写入策略

如果是骑手、车辆、设备轨迹等高频移动对象,位置更新会非常频繁。此时需要评估:

  • 更新频率是否合理
  • 是否按时间窗口保留位置
  • 是否需要额外记录历史轨迹

Redis GEO 更适合存“当前点位”,历史轨迹通常另存。

5. 注意命令版本差异与使用习惯

在较新的 Redis 版本中,GEOSEARCH 是更推荐的统一检索命令。历史上有些项目会使用老的半径查询命令,但新项目建议优先围绕 GEOSEARCH 设计。

6. 不要把 GEO 结果当作最终业务结果

GEO 更适合作为候选集筛选工具。最终排序往往还要结合:

  • 营业状态
  • 库存
  • 配送能力
  • 用户偏好
  • 商家权重

也就是说,GEO 解决的是“地理上接近”,不是“业务上最优”。


九、实战设计建议

1. 坐标类 key 与详情类 key 分离

推荐这样设计:

  • store:geo:beijing:保存地理坐标
  • store:info:{storeId}:保存门店详细信息

这样结构清晰,更新也方便。

2. 按城市或业务域拆分 key

如果所有门店都塞进一个大 key,后续维护和查询粒度可能不够灵活。可以按城市、区域、业务线拆分。

例如:

store:geo:bj
store:geo:sh
store:geo:food
store:geo:warehouse

3. 先 GEO 粗筛,再业务层精筛

常见流程是:

  1. 用 GEO 找附近 50 个候选点
  2. 应用层根据状态和规则过滤
  3. 再按最终得分排序

这种设计性能和灵活性都比较好。


十、小结

GEO 是 Redis 中非常实用的地理位置能力,底层依托 Sorted Set,通过地理编码和范围检索实现“附近搜索”“距离计算”等功能。

可以重点记住以下几点:

  • GEO 用来存储经纬度并做附近检索
  • 底层基于 Sorted Set,不是完全独立的新存储模型
  • 常用命令包括 GEOADDGEODISTGEOPOSGEOSEARCH
  • 适合附近的人、附近门店、调度候选筛选等场景
  • 注意经纬度顺序、距离含义以及能力边界

如果业务只是需要一个轻量、高效的“附近检索”能力,那么 Redis GEO 往往非常顺手;但如果业务已经走向复杂空间分析,那就要考虑更专业的地理系统来配合。


📝 版权声明:本文为原创技术博客,转载请注明出处。

如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!

上一篇

Redis高级数据结构3.2:HyperLogLog 基数统计

下一篇

Rddis高级数据结构3.4: Stream 消息流