首页 / Redis 入门教程 / 地理空间 GEO

Redis 入门教程

地理空间 GEO

本教程共 40 篇 · 第 13 篇 · 更新于 2026-08-02

redisgeoGEOADDGEOSEARCH附近的人地理空间经纬度

13. 地理空间 GEO

本节目标

  • 理解 GEO 本质是把经纬度编码成 score 存进 Sorted Set
  • 掌握 GEOADD / GEOPOS / GEODIST / GEOHASH
  • 掌握「附近的人 / 附近的店」核心命令 GEOSEARCH(含半径与矩形范围)
  • 了解 GEORADIUS 在 Redis 8.x 中已被废弃,改用 GEOSEARCH
  • 用「查找 5 公里内的门店」实战巩固

Redis GEO 用于在 Redis 中存储地理位置(经度、纬度、名称),并对这些位置做距离计算、范围查找。它自 Redis 3.2 引入,底层实现非常巧妙:把每个位置的经纬度通过 Geohash 编码成一个 52 位整数,作为 Sorted Set 的 score 存起来。所以 GEO 命令本质上是对 Sorted Set 的封装——你甚至可以用 ZRANGE 看到这些位置,用 ZSCORE 看到编码后的分值(不过一般没必要这么做)。

13-1 添加与读取位置

GEOADD key longitude latitude member [ ... ] 添加地理位置:

命令:

127.0.0.1:6379> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"

输出:

(integer) 2

注意参数顺序是「经度 longitude 在前,纬度 latitude 在后」,弄反了坐标会落错地方。返回值是新加入的位置数量。

取回某个位置的经纬度用 GEOPOS,距离用 GEODIST

命令:

127.0.0.1:6379> GEOPOS Sicily Palermo Catania
127.0.0.1:6379> GEODIST Sicily Palermo Catania km

输出:

1) 1) "13.36138933897018433"
   2) "38.11555639549629859"
2) 1) "15.08726745843887329"
   2) "37.50266842333162032"
"166.2742"

GEODIST 默认单位是米(m),可指定 km / m / mi(英里) / ft(英尺)。若其中某个 member 不存在,返回 (nil)

13-2 「附近的人」:GEOSEARCH

范围查找是 GEO 最常用的能力。Redis 8.x 官方推荐用 GEOSEARCH,它以给定中心点(经纬度或某个已存在 member)出发,按「半径」或「矩形」查找范围内的位置:

命令:

127.0.0.1:6379> GEOSEARCH Sicily FROMLONLAT 15 37 BYRADIUS 200 km WITHDIST ASC

输出:

1) 1) "Catania"
   2) "56.4413"
2) 1) "Palermo"
   2) "190.4424"

关键参数:

  • FROMLONLAT lon lat:以指定经纬度为中心;FROMMEMBER member:以某个已存位置为中心。
  • BYRADIUS 200 km:圆形半径范围;BYBOX 200 200 km:矩形(宽×高)范围。
  • WITHDIST:一并返回距离;WITHCOORD:一并返回坐标;WITHHASH:返回编码值。
  • ASC / DESC:按距离排序;COUNT n:限制返回数量。

查找「指定位置 X 公里内的人」:

命令:

127.0.0.1:6379> GEOSEARCH Sicily FROMMEMBER Palermo BYRADIUS 100 km ASC

输出:

1) "Palermo"
2) "Catania"

GEOHASH 返回位置对应的 Geohash 字符串(可用于调试或对外暴露粗略位置):

命令:

127.0.0.1:6379> GEOHASH Sicily Palermo Catania

输出:

1) "sqc8b49rny0"
2) "sqdtr74hyu0"

提示:老教程里常见的 GEORADIUS / GEORADIUSBYMEMBER 在 Redis 8.x 中已被标记为废弃(deprecated),未来版本可能移除。新代码请统一使用 GEOSEARCH / GEOSEARCHSTORE(后者把结果写入新 key)。二者能力完全覆盖旧的 GEORADIUS,只是参数风格更一致。本教程以 Redis 8.x(最新稳定版)为准,故只演示 GEOSEARCH

13-3 实战:查找附近的门店

假设一个外卖 App 要给用户展示「5 公里内的奶茶店」:

命令:

127.0.0.1:6379> GEOADD shops 116.404 39.915 "store:A" 116.410 39.920 "store:B" 116.500 39.990 "store:C"
127.0.0.1:6379> GEOSEARCH shops FROMLONLAT 116.405 39.916 BYRADIUS 5 km WITHDIST ASC COUNT 20

输出:

1) 1) "store:A"
   2) "0.1432"
2) 1) "store:B"
   2) "0.9311"

store:C 距离超过 5 公里,不会被返回。结合 COUNT 20 即可实现「附近门店列表 + 距离 + 由近及远排序」。

13-4 常见误区

  • 误区:GEOADD 参数顺序是「纬度在前」。 错,必须是「经度 longitude 纬度 latitude」,与日常「纬度, 经度」的书写习惯相反,务必小心。
  • 误区:GEO 是独立类型、不能用 Z 命令操作。* GEO 底层就是 Sorted Set,可以用 ZRANGE / ZREM 等,但一般不推荐直接改 score。
  • 误区:还在用 GEORADIUS。 Redis 8.x 已废弃,新代码用 GEOSEARCH
  • 误区:GEO 能存任意元数据。 GEO 的 member 只能是个名字(字符串),想关联门店名称、评分等信息,通常把 member 设为门店 ID,详情另存 Hash/String。

13.x Geohash 原理、精度与部署注意

理解一点底层原理能帮你避开坑。GEO 把「经度、纬度」通过 Geohash 编码成一段 52 位整数作为 Sorted Set 的 score,所以对「附近」的查询,本质上是在这个一维编码空间里找「编码值相近」的成员。Geohash 的特点是:地理位置越接近,编码前缀越相似——这正是 GEOHASH 返回的字符串「前面几位相同 = 大致在同一区域」的原因。代价是 Geohash 在「经度/纬度边界」附近会有轻微畸变(矩形边界处的邻近关系不绝对精确),所以 GEOSEARCH 在底层实际是用「多区间检索 + 距离复核」来保证圆形/矩形范围内结果的准确性,并非简单按编码前缀切。

工程上还要注意几点:

  • 单位务必显式写。距离参数不写单位时默认是米(m),但很多人误以为默认是公里。GEODISTGEOSEARCH 都建议明确带上 km / m,避免「以为 5 公里、实际 5 米」的事故。
  • 赤道附近最准,高纬度略有拉伸。Geohash 在极点附近会产生更大形变,范围查询的精度会略降,超高精度场景需自行做球面距离复核。
  • member 只是名字。GEO 的 member 是字符串标识,想关联门店名、评分、营业时间等,请把 member 设为门店 ID,详情另存 Hash 或 JSON(见第 15 章),不要试图把所有信息塞进 member。
  • 数据量大时控制单次返回。务必用 COUNT 限制返回条数,并对结果做分页;WITHCOORD 会连同坐标一起返回,数据多时体积不小,按需使用。
  • 与数据库配合。GEO 适合「给定中心点找附近」,但复杂的「按城市、按分类筛选后再找附近」通常需要和关系库的二级索引、或 Redis 的查询引擎(Search and Query)结合,别把 GEO 当成完整地理信息系统(GIS)。

另一个常被问到的问题是:「门店有几百万个,GEO 还扛得住吗?」GEO 底层是 Sorted Set,范围查询需要对编码空间做多区间检索再做距离复核,随着成员数增长,单次 GEOSEARCH 的成本会上升,并非「建了索引就恒快」。海量地理位置点通常要配合分片:例如按城市、按网格把不同区域的数据放到不同的 key 甚至不同的 Redis 实例,查询时先定位所在分片再查;或借助 Redis 的查询引擎(Search and Query)对 Hash/JSON 建地理索引获得更丰富的联合查询能力。GEO 命令胜在轻量、无需额外索引配置,适合中小规模或作为「先跑起来」的方案,规模上来后再演进到更完整的检索方案。

13-5 小结

  • GEO 把经纬度编码为 Sorted Set 的 score,实现地理存储与查找。
  • 写/读:GEOADD / GEOPOS / GEODIST(m/km/mi/ft)/ GEOHASH
  • 范围查找:GEOSEARCH(推荐,替代废弃的 GEORADIUS),支持 FROMLONLAT/FROMMEMBERBYRADIUS/BYBOXWITHDIST/WITHCOORDASC/DESC/COUNT
  • 用武之地:附近的人、附近的店、配送范围、地理围栏雏形。

提示:关于版本——本教程正文统一以 Redis 8.x(最新稳定版)为准;官方 redis.io 下载页另标 8.8 为 “Latest stable”,而 GitHub 上 redis/redis 的最新发布 tag 为 8.10.0,二者同属 8.x,命令与类型差异对教材影响极小。