GEOSEARCH 比手写 Lua 更可靠,因其原生支持 Haversine 公式、自动 geohash 网格裁剪、避免边界遗漏与精度偏差,且支持 WITHCOORD/WITHDIST 等关键选项实现精准排序与渲染。

为什么 GEOSEARCH 比自己写 Lua 更可靠?
Redis 6.2+ 原生 GEOSEARCH 已覆盖绝大多数“附近查找”需求,比手写 Lua 更安全、更高效。自己拼 GEORADIUS 或用 zrangebyscore 手算球面距离,容易漏掉边界点、精度偏差大,且无法利用 Redis 内置的 geohash 优化。
常见错误是把经纬度当平面坐标直接算欧氏距离——地球是球面,GEODIST 和 GEOSEARCH 内部用的是 Haversine 公式,误差控制在 0.1% 以内。
-
GEOSEARCH支持BYRADIUS/BYBOX,自动处理 geohash 网格裁剪,查得快也查得准 - 若用 Lua,必须手动调
GEORADIUS+GEODIST二次过滤,多一次 round-trip,还可能因精度截断丢点 - Redis 7.0+ 的
GEOSEARCHSTORE还能缓存结果,避免重复计算
必须传 WITHCOORD 和 WITHDIST 才能拿到完整信息
只用 GEOSEARCH key BYRADIUS 10 km 返回的只是成员名,没坐标也没距离——这对前端渲染或排序毫无用处。实际业务中几乎总要这两个选项。
示例命令:
GEOSearch store:locations BYRADIUS 120 km 30.5 104.08 WITHCOORD WITHDIST WITHHASH COUNT 50
注意:WITHHASH 返回 geohash 整数,可用于去重或粗略排序;COUNT 不是硬限制(Redis 会先取超量再过滤),但设太大会拖慢响应。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 不加
WITHDIST就没法按距离排序,前端只能盲展示 -
WITHCOORD返回的是原始存入时的经纬度,不是中心点偏移量——别误以为是相对坐标 - 如果查出结果要再查用户资料,建议用 pipeline 批量
HGET,别在 Lua 里循环HGET,会阻塞
Lua 脚本只在两种情况下值得写
一是 Redis 版本 GEOSEARCH;二是需要自定义过滤逻辑(比如排除状态为 offline 的用户)。但要注意:Lua 里不能调 GEODIST,只能靠近似公式算距离,误差随纬度升高变大。
一个最小可用脚本示例(仅适用于低纬度区域):
local members = redis.call('GEORADIUS', KEYS[1], ARGV[1], ARGV[2], 'km', 'COUNT', ARGV[3])
local result = {}
for _, member in ipairs(members) do
local pos = redis.call('GEOPOS', KEYS[1], member)
if #pos == 2 then
local lon1, lat1 = tonumber(ARGV[1]), tonumber(ARGV[2])
local lon2, lat2 = tonumber(pos[1]), tonumber(pos[2])
local dlon = (lon2 - lon1) * math.pi / 180
local dlat = (lat2 - lat1) * math.pi / 180
local a = math.sin(dlat/2)^2 + math.cos(lat1 * math.pi / 180) * math.cos(lat2 * math.pi / 180) * math.sin(dlon/2)^2
local c = 2 * math.asin(math.sqrt(a))
local dist = 6371 * c -- km
if dist <= tonumber(ARGV[4]) then
table.insert(result, {member, dist})
end
end
end
return result- 参数顺序必须严格:KEYS[1] 是 key,ARGV[1]/[2] 是中心点 lon/lat,ARGV[3] 是 GEORADIUS 的 COUNT,ARGV[4] 是实际半径(km)
- math 库在 Redis Lua 中可用,但
math.acos在某些旧版本有 bug,优先用math.asin+ 平方根 - 别在 Lua 里做
HGETALL或HMGET,数据量大时极易超时;先取 ID,再由客户端或服务端异步查详情
生产环境必须检查 geohash 精度和 key 设计
用 GEOADD 存位置时,Redis 默认用 52 位 geohash,足够支持 ~1m 精度。但如果业务只需要城市级(如“附近商圈”),可提前用外部库把经纬度转成 4~5 位 geohash 再存——减少内存占用,提升 GEORADIUS 性能。
更关键的是 key 命名:别用单个 all_users,按业务维度拆分,比如 store:active、driver:online。否则 GEORADIUS 扫描整个有序集合,QPS 上千时 CPU 直接飙高。
- 定期用
MEMORY USAGE key检查地理位置 key 占用,超 10MB 就该考虑分片 - 过期时间不能设在 geo key 上(Redis 不支持),需用独立的
EXPIRE或定时任务清理过期成员 - 测试时用真实经纬度(别用 0,0),尤其注意赤道附近和高纬度地区距离误差是否可接受

















