redis lua脚本能真正原子执行,是因为其在单线程主线程中一口气执行完毕,期间其他客户端命令全部排队等待,连redis.call间都不会插入任何操作。

Redis 官方推荐用 Lua 脚本实现复合逻辑,核心就两条:它真能原子执行,而且确实少发包。
为什么 Lua 脚本能真正原子执行
不是靠加锁,是 Redis 单线程模型硬扛下来的。执行 EVAL 或 EVALSHA 时,整个脚本在主线程里一口气跑完,期间其他所有客户端命令都得排队等——连 redis.call("GET", KEYS[1]) 和 redis.call("INCRBY", KEYS[1], ARGV[1]) 中间都不会插进别的命令。
- 原子性只覆盖脚本内调用的
redis.call()/redis.pcall()操作;os.time()、math.random()这类纯 Lua 函数不参与原子保障 - 一旦脚本里用了
redis.call("KEYS", "*")这种阻塞命令,可能让出控制权,破坏原子语义 - Redis 6.0+ 强制要求只读命令也必须通过
KEYS传 key,否则集群路由失败或被拒绝
为什么能显著减少网络 IO
原来要三步:客户端发 GET → 等响应 → 判断 → 再发 DECRBY → 再发 LPUSH。三次往返,每次都有 TCP 延迟、序列化开销、网络抖动风险。用 Lua 后,一次 EVALSHA 把逻辑全塞进去,网络层只走一趟。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 对高延迟链路(比如跨机房调用)收益尤其明显;本地回环下也能省掉两次 syscall 和 redis 协议解析
-
SCRIPT LOAD后复用EVALSHA,还能避免重复传输几百行脚本文本,防止 AOF 日志膨胀 - 注意:
KEYS和ARGV参数总长度仍受proto-max-bulk-len限制,默认 512MB,但实际建议控制在几 KB 内
哪些地方容易误判“已经原子”
看似封装成一个脚本,但稍不注意,原子性就漏了。
- key 名拼接写死,比如
"user:"..ARGV[1]..":balance"—— 集群模式下路由失效,且触发EVAL安全限制 - 用
redis.pcall()捕获错误却没检查返回值,导致后续逻辑继续执行,实际已跳过关键校验 - 脚本里混了 HTTP 请求(需加载
lua-resty-http等模块)或文件读写,这部分完全脱离 Redis 控制,也不原子 - 超时没设:脚本执行超过
lua-time-limit(默认 5000ms),会被强制SCRIPT KILL,但已执行的部分不会回滚
真正难的不是写对语法,而是厘清哪段逻辑必须塞进脚本里、哪段可以放客户端——边界划错,原子性就成幻觉。










