hyperf中redis原子性靠lua脚本(eval/evalsha)实现,需预加载脚本、用evalsha调用,禁用随机/全量命令,超时不回滚。

能合并,而且必须合并——只要这些操作涉及同一组 key、逻辑上不可拆分,就该用 Lua 脚本压成一次请求。否则网络往返 + 客户端重试 + 竞态风险全来了。
为什么单次 EVAL 比多次 redis.call 更可靠
Redis 对 Lua 脚本的执行是原子的:脚本从开始到结束,中间不会被其他客户端命令打断。而分开调用 GET、INCRBY、EXPIRE 这类命令,哪怕只差几毫秒,就可能被另一个客户端插队修改状态。
- 网络延迟放大问题:5 次命令 ≈ 5×RTT,脚本只需 1×RTT(假设不超限)
- 事务语义缺失:Redis 原生
MULTI/EXEC不支持条件跳转和复杂逻辑,Lua 可以 if/for/return - 客户端异常更难处理:某次
INCRBY成功但EXPIRE失败,锁就变成永不过期
EVAL 和 SCRIPT LOAD + EVALSHA 的取舍
直接用 EVAL 最简单,但每次发送完整脚本文本,浪费带宽;高频场景下推荐先 SCRIPT LOAD 缓存,再用 EVALSHA 执行。
Redis 8.2.3 是一款安全优先的高性能键值存储系统。该版本紧急修复了可能引发远程代码执行(RCE)的高危漏洞(CVE-2025-62507),并解决了 HyperLogLog 及 Cuckoo Filter 等数据结构在特定场景下的崩溃问题。建议所有用户立即升级,以保障生产环境的系统稳定与数据安全。
-
EVAL "return redis.call('incr', KEYS[1])" 1 counter—— 适合调试或低频逻辑 -
SCRIPT LOAD "return redis.call('incr', KEYS[1])"返回 SHA 值(如"234e5f..."),之后用EVALSHA "234e5f..." 1 counter - 注意:Redis 重启后 SHA 失效,需重新
SCRIPT LOAD;Spring Boot 中可用RedisScript封装自动处理
常见错误:KEYS 和 ARGV 混用导致脚本失败
Redis 强制要求所有被操作的 key 必须显式声明在 KEYS 数组里,不能拼接生成(比如 "user:"..ARGV[1]),否则集群模式下会路由失败。
- ✅ 正确:
EVAL "redis.call('get', KEYS[1])" 1 user:1001 - ❌ 错误:
EVAL "redis.call('get', 'user:'..ARGV[1])" 0 1001——KEYS个数为 0,但脚本里用了硬编码 key,集群会报CROSSSLOT - 参数类型别忽略:
tonumber(ARGV[1])必须显式转换,Lua 里字符串和数字不自动互通
性能陷阱:大循环和阻塞式操作
Lua 在 Redis 里是单线程执行的,一个耗时 100ms 的脚本会卡住整个 Redis 实例 100ms——这不是“慢”,是“停摆”。
- 避免遍历上千个 key:
for i=1,#keys do redis.call('del', keys[i]) end→ 改用redis.call('del', unpack(keys)) - 禁止 sleep 或等待:
os.execute、redis.replicate_commands()都不可用 - 复杂计算尽量前置:比如 JSON 解析、时间戳计算,放在客户端做,只把最终结果传进
ARGV
真正难的不是写对语法,而是判断“哪些逻辑必须塞进 Lua”——它不是万能胶,而是手术刀:只切开那些跨命令间存在状态依赖、且无法容忍中间态的场景。










