eval比pipeline更快是因为它将逻辑移至服务端单线程原子执行,仅需1次rtt且无部分失败;pipeline虽减少rtt但不保证原子性,仍为客户端打包、服务端逐条执行。

EVAL 一次调用就能完成大批量写入,不是靠“更快”,而是直接砍掉 99% 的网络等待时间。吞吐量提升的核心在于减少 RTT(往返延迟),而不是优化单条命令执行速度。
为什么 EVAL 比 Pipeline 写入更快?
Pipeline 是客户端打包、服务端逐条执行,它不解决命令间依赖或原子性问题;EVAL 把逻辑搬到服务端,在单线程里一口气跑完,网络只走 1 次,且全程原子。
- 1000 次
SET:逐条执行 ≈ 105ms,Pipeline ≈ 1.2ms,EVAL脚本 ≈ 0.8ms(本地实测) - 差距主因不是 Redis 处理慢,而是每多一次网络往返就多约 0.1ms 延迟 —— 1000 次就是 100ms 白白耗在等响应上
- Pipeline 的结果可能部分成功部分失败;
EVAL要么全成,要么全挂,适合库存扣减、计数更新等强一致性场景
redis.call 调用次数是性能隐性杀手
Lua 脚本快,但每次 redis.call 都有固定开销(序列化、命令分发、返回值转换)。脚本内调用 10 次 redis.call,性能可能比调用 2 次下降 40% 以上。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 优先用
MSET/MGET/HMSET等批量命令替代循环里的单条SET - 避免在 for 循环里反复
redis.call('GET', ...),先用redis.call('MGET', ...)一次性取回,再在 Lua 里做判断 - 中间计算尽量留在 Lua 变量里,比如累加、过滤、拼接,别动不动就
redis.call
大批量写入的典型脚本结构怎么写?
别直接循环 SET,先组织好参数,再用批量命令落地。下面这个脚本把 1000 对 key-value 用 MSET 一次写入,比循环调用 redis.call('SET',...) 快 3–5 倍:
local keys = KEYS
local values = ARGV
-- 构造 mset 参数:key1, val1, key2, val2, ...
local args = {}
for i = 1, #keys do
table.insert(args, keys[i])
table.insert(args, values[i])
end
return redis.call('MSET', unpack(args))
- 调用方式:
EVAL "...script..." 1000 key1 key2 ... key1000 val1 val2 ... val1000 -
KEYS和ARGV总数不能超过 1024 个(Redis 限制),超了得拆成多个EVAL调用 - 如果 key 数量不确定,用
unpack(args)安全展开;Lua 5.1+ 支持,Redis 内置 Lua 版本满足
脚本缓存用 EVALSHA 还是每次都传全文?
首次用 EVAL,后续统一改用 EVALSHA + SHA1 值,能省下几百字节传输,尤其当脚本超过 1KB 时更明显。
- 客户端需自行缓存脚本 SHA1,比如 Python 用
redis.script_load()获取 SHA,之后用evalsha() - Redis 会自动缓存已加载脚本,重启后失效 —— 生产环境建议启动时预热脚本(
SCRIPT LOAD) - 别为了省几毫秒而放弃可读性:开发阶段用
EVAL直接传脚本,上线后再切EVALSHA
实际吞吐瓶颈往往不在 Redis 本身,而在你有没有把“网络等”这件事彻底从关键路径里拿掉。写 Lua 脚本时,第一反应不该是“怎么写逻辑”,而是“这一轮网络能不能省掉”。










