登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

Redis 怎么查询附近门店并按距离排序

来源:17golang原创

时间:2026-09-06 00:00:20 331浏览 收藏

如果门店已经有经度和纬度,Redis 不需要把每条门店记录都取到应用层再计算距离。先用 GEOADD 把门店成员写入同一个地理索引,再用 GEOSEARCH 指定中心点、半径和 ASC,就能得到“附近门店 + 最近优先”的结果。接口通常再加上 WITHDIST 返回距离,前端便可以直接展示“1.8 km”。

最小可用写法是:GEOSEARCH store:geo FROMLONLAT 121.4737 31.2304 BYRADIUS 5 KM ASC WITHDIST COUNT 10。其中经度在前、纬度在后,ASC 表示由近到远。
要点速览
  • GEOADD 的参数顺序是 longitude、latitude、member,地理数据保存在有序集合中。
  • GEOSEARCH 支持 FROMLONLAT 或已有成员作为中心,圆形范围用 BYRADIUS
  • 要展示距离使用 WITHDIST;生产接口可用 COUNT 限制返回量,但不要随意加 ANY

GEOADD 先把门店放进地理索引

Redis 的 GEO 数据以 sorted set 形式存储,门店名称是 member,经纬度被编码进 score。业务上可以把 key 命名为 store:geo,再把门店的其他信息放在普通 Hash 或数据库里;地理索引负责“谁在附近”,详情查询负责“门店有哪些字段”。

# 经度在前,纬度在后;每组三个参数对应一个门店
redis-cli GEOADD store:geo \
  121.4737 31.2304 store-a \
  121.4800 31.2280 store-b \
  121.4652 31.2351 store-c

# 查看一个成员的坐标,检查写入顺序是否正确
redis-cli GEOPOS store:geo store-a

示例中的 store-a 只是索引成员,不等同于完整门店对象。更新门店位置时再次执行 GEOADD 会更新已有成员的坐标;删除门店则使用 ZREM store:geo store-a。这种拆分能避免为了更新地址而重写一整份门店详情。

Redis GEOADD 将门店经纬度与成员写入 store:geo 有序地理索引的结构图
图1:Redis 地理索引把经度、纬度和门店成员绑定到 store:geo,详情数据可以留在独立存储中。

用 GEOSEARCH 按半径筛选并按距离排序

查询坐标附近门店时,使用 FROMLONLAT 传入用户当前位置,BYRADIUS 传入半径和单位,再加 ASC。下面以 5 公里为范围,最多取 10 家,并返回距离:

# 以用户坐标为中心,在 5 KM 内由近到远取 10 家门店
redis-cli GEOSEARCH store:geo \
  FROMLONLAT 121.4737 31.2304 \
  BYRADIUS 5 KM ASC COUNT 10 WITHDIST

# 需要把坐标也交给地图卡片时,再追加 WITHCOORD
redis-cli GEOSEARCH store:geo \
  FROMLONLAT 121.4737 31.2304 \
  BYRADIUS 5 KM ASC COUNT 10 WITHDIST WITHCOORD

第一条命令只返回成员名和距离,第二条还会返回每个成员的经纬度。ASC 是按离中心点的距离升序排列,省略排序选项时不要把返回顺序当成最近优先。若查询中心本身就是某个已索引门店,也可以把 FROMLONLAT 换成 FROMMEMBER store-a

查询片段作用接口选择建议
FROMLONLAT lon lat使用请求中的坐标作为中心用户定位、配送点、临时坐标
BYRADIUS 5 KM限定圆形范围和单位“附近 5 公里”这类需求
ASC最近的成员排在前面列表按距离展示
WITHDIST返回距离数值展示距离或二次业务判断
Redis GEOSEARCH 以用户坐标和半径过滤门店并按 ASC 返回距离的参数关系图
图2:GEOSEARCH 将中心点、圆形范围、排序和返回字段组合成附近门店查询。

读取距离和坐标时要看清返回形状

不带任何 WITH* 选项时,结果只是成员名数组;带 WITHDISTWITHCOORD 后,每个成员会变成一个子数组。距离单位跟 BYRADIUS 使用的单位一致,因此命令里写 KM,应用层就按公里解释,不必再把数值乘除一遍。

package main

import "fmt"

// 解析 GEOSEARCH 的成员、距离和坐标,避免把距离当成米
type NearbyStore struct {
    ID        string
    Distance  float64 // 单位由查询中的 KM 或 M 决定
    Longitude float64
    Latitude  float64
}

// 生产代码应按客户端库的 GeoLocation 结构读取,先明确统一单位
func displayDistance(distance float64, unit string) string {
    // 这里只格式化查询返回值,不重新计算球面距离
    return fmt.Sprintf("%.1f %s", distance, unit)
}

如果业务还要显示营业状态、门店名称或库存,建议先拿到成员 ID,再批量读取 Hash 或数据库记录。地理查询只解决空间范围和距离排序,不会自动把外部门店详情拼接进结果。

COUNT、ANY 和经纬度顺序的常见坑

COUNT 10 表示最多返回 10 个匹配成员,但服务器仍需要在范围内处理并排序。官方文档特别说明,范围很大而 COUNT 很小时,排序工作仍可能明显;ANY 会在找到足够数量后提前结束,速度可能更快,但结果可能不是有序的。需要“最近的 10 家”时,不要为了少一次计算就加 ANY

另外,Redis 使用标准的 x、y 顺序,即经度在前、纬度在后。经度合法范围为 -180 到 180,纬度可索引范围约为 -85.05112878 到 85.05112878。把国内常见的“纬度、经度”接口字段直接传给 GEOADD,通常不会报语法错误,却会把门店放到错误位置。写入前最好在应用层统一字段名,例如 longitudelatitude,并对边界值做一次业务校验。

最后,半径查询适合“附近”筛选,不等于驾车路线距离。若页面要展示预计到店时间或真实道路距离,还需要调用地图路线服务;Redis 返回的是基于地理坐标的直线距离。

常见问题

GEOSEARCH 和 GEORADIUS 应该选哪个?

新代码优先使用 GEOSEARCH。它能覆盖圆形和矩形范围,Redis 文档将 GEORADIUS 与 GEORADIUSBYMEMBER 标为弃用方向。

为什么查出来的距离不是整数?

距离是浮点数,Redis 会按查询单位返回带小数的结果。展示层可以保留一位小数,但不要把小数直接当作精确的道路里程。

能不能用门店名称作为查询中心?

可以,只要该成员已经在同一个地理 key 中,使用 FROMMEMBER member;如果中心来自用户定位,则使用 FROMLONLAT

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>