Redis GEO 地理位置

GEO 是一组处理经纬度坐标的命令,解决"存坐标、算距离、按范围找人"这类 LBS 需求,如附近的人、附近的店、配送范围圈选。坐标以成员(member)名关联存入 key,底层是一棵有序集合 ZSet(score 为坐标的 52 位二进制编码),因此排序、删除可直接借用 ZSet 命令。

写入坐标:GEOADD

GEOADD key 经度 纬度 成员——注意顺序是"经度在前、纬度在后":

GEOADD city 13.361389 38.115556 "帕勒莫" 15.087269 37.502669 "卡塔尼亚"
# 输出:(integer) 2
TYPE city
# 输出:zset                # 底层就是有序集合

同一成员重复写入会更新坐标,返回值是新增成员数,此时为 0。

查询坐标:GEOPOS

GEOPOS 返回成员当前坐标。注意因 geohash 编码精度有限,返回值与写入值会有微小偏差:

GEOPOS city 帕勒莫 卡塔尼亚
# 输出:1) 1) "13.36138933897018433"
#       2) "38.11555639549629859"
#       2) 1) "15.08726745843887329"
#       2) "37.50266842333162032"

计算距离:GEODIST

GEODIST key 成员1 成员2 [单位],单位支持 m、km、mi(英里)、ft(英尺),默认 m:

GEODIST city 帕勒莫 卡塔尼亚 km
# 输出:"166.2742"          # 两地直线距离约 166.3 公里

范围检索:GEOSEARCH

GEOSEARCH 是官方推荐的范围查询命令,支持从成员(FROMMEMBER)或直接经纬度(FROMLONLAT)出发,按半径(BYRADIUS)或矩形(BYBOX)圈范围,可加 WITHDIST 返回距离、ASC 按距离升序:

GEOSEARCH city FROMMEMBER 帕勒莫 BYRADIUS 200 km ASC WITHDIST
# 输出:1) 1) "帕勒莫" 2) "0.0000"
#       2) 1) "卡塔尼亚" 2) "166.2742"

半径缩到 100 km 就只剩帕勒莫自己:

GEOSEARCH city FROMMEMBER 帕勒莫 BYRADIUS 100 km
# 输出:1) "帕勒莫"

更早的 GEORADIUS / GEORADIUSBYMEMBER 已标记弃用(仍可用),新代码直接用 GEOSEARCH / GEOSEARCHSTORE。

底层与运维

因为底层是 ZSet,删除成员用 ZREM、按序浏览用 ZRANGE;GEOHASH 可查看成员对应的 geohash 字符串:

ZREM city 卡塔尼亚
# 输出:(integer) 1
GEOHASH city 帕勒莫
# 输出:1) "sqc8b49rny0"

附近的人实现思路

  1. 用户上报位置时执行 GEOADD user:loc 经度 纬度 用户ID;
  2. 查询时以"我的坐标"为圆心 GEOSEARCH … FROMLONLAT 我的经度 我的纬度 BYRADIUS 5 km ASC,取前 N 个;
  3. 展示层用返回的成员名或 WITHDIST 距离排序。 注意:频繁上报会写放大,移动端通常隔几分钟上报一次;超大用户量可先按城市拆分 key,减小单 key 规模。

小结

GEO 命令组解决"存坐标、算距离、圈范围"三类问题:GEOADD 写入、GEODIST 算距、GEOSEARCH 查附近(用它替代已弃用的 GEORADIUS 系列)。三个记忆点:经度在前纬度在后、底层是 ZSet、范围查询用 GEOSEARCH。

笔记加载中…