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"
附近的人实现思路
- 用户上报位置时执行 GEOADD user:loc 经度 纬度 用户ID;
- 查询时以"我的坐标"为圆心 GEOSEARCH … FROMLONLAT 我的经度 我的纬度 BYRADIUS 5 km ASC,取前 N 个;
- 展示层用返回的成员名或 WITHDIST 距离排序。 注意:频繁上报会写放大,移动端通常隔几分钟上报一次;超大用户量可先按城市拆分 key,减小单 key 规模。
小结
GEO 命令组解决"存坐标、算距离、圈范围"三类问题:GEOADD 写入、GEODIST 算距、GEOSEARCH 查附近(用它替代已弃用的 GEORADIUS 系列)。三个记忆点:经度在前纬度在后、底层是 ZSet、范围查询用 GEOSEARCH。