将计算逻辑移至Redis Lua脚本可显著降本,关键在于精准选择需下推的计算、控制redis.call次数、预加载脚本(SCRIPT LOAD+EVALSHA),并避免脚本阻塞主线程。

直接把计算逻辑从客户端搬到Redis服务器端,是降低整体计算成本最有效的手段。关键不是“能不能写Lua”,而是“哪些计算必须推过去”以及“怎么推才不拖慢Redis主线程”。
为什么客户端循环调用 HGETALL 是性能黑洞
原始模式里,GEOSEARCH 返回 N 个成员后,客户端逐个执行 HGETALL,形成典型的 N+1 查询。每轮都经历:网络发包 → Redis 解析命令 → 查哈希表 → 序列化返回 → 客户端反序列化 → 再算加权和。N 超过 50,延迟就明显抖动;到几百时,90% 时间花在网络和序列化上,而非真正计算。
- Redis 单线程处理每个
redis.call(),但客户端发起的每次独立命令都会排队等待 -
HGETALL返回的是完整哈希结构,哪怕你只用cc字段,也得传整张表 - 客户端 CPU 还要解析 JSON 或 PHP array、做 float 转换、执行公式,这部分无法横向扩展
用 EVALSHA + 预加载脚本避免重复编译开销
每次用 EVAL 提交脚本,Redis 都要 tokenize、parse、compile,开销固定约 0.1–0.3ms。高频调用下这会累积成瓶颈。改用 EVALSHA 是刚需。
- 首次调用前,先用
SCRIPT LOAD把 Lua 脚本存进 Redis,返回一个 SHA1 值(如"2e4f8a1b...") - 后续全部用
EVALSHA "2e4f8a1b..." 2 table:geo key:prefix 116.4 39.9 5000 m,跳过编译阶段 - 脚本内容不变时,SHA1 不变;更新脚本需重新
SCRIPT LOAD并刷新客户端缓存的 SHA
不预加载而硬写 EVAL,在 QPS > 200 的服务里,光编译就吃掉 10%+ 的 CPU 时间。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
Lua 脚本内部必须控制 redis.call 次数
redis.call() 是 Lua 和 Redis 数据层的唯一桥梁,但每次调用都有上下文切换和命令分发成本。实测显示:单脚本中 redis.call 超过 3 次,平均执行时间呈非线性增长。
- 优先用
redis.call('HGET', key, 'cc')替代HGETALL,只取需要字段 - 如果一批成员共用同一组参数(比如统一
$radius),把它们打包进ARGV一次性传入,避免在 Lua 里拼接 key - 数学计算全放在 Lua 里做:距离归一化、权重相乘、累加——这些纯 CPU 操作在 Lua 沙箱里几乎无开销
- 别在 for 循环里反复
redis.call('EXISTS'),先redis.call('EXISTS', key)判断,再决定是否HGET
地理空间场景下的最小可行 Lua 示例
对应开头的加权求和需求,这个脚本把 GEOSEARCH + 多次 HGET + 公式计算全收束为一次请求:
local members = redis.call('GEOSEARCH', KEYS[1], 'FROMLONLAT', ARGV[1], ARGV[2], 'BYRADIUS', ARGV[3], ARGV[4], 'WITHDIST')
local sum = 0
for i = 1, #members, 2 do
local member = members[i]
local dist = tonumber(members[i+1])
local cc = tonumber(redis.call('HGET', member, 'cc') or '0')
if cc and dist then
sum = sum + cc * (tonumber(ARGV[3]) - dist / tonumber(ARGV[3]))
end
end
return sum调用方式:EVALSHA <sha> 1 table:geo 116.4 39.9 5000 m。注意:KEYS 只传 1 个主键,其余全走 ARGV,避免 key 名动态拼接引发集群路由失败。
真正容易被忽略的点是——Lua 脚本不是万能加速器。它把压力从网络和客户端搬到了 Redis 主线程。如果脚本执行超 5ms(比如遍历上千成员+多次 call),会阻塞其他命令。此时该考虑拆分查询半径、加缓存层,或者把部分逻辑下沉到应用层预计算,而不是硬堆 Lua。

















