Redis 事务和 Lua 脚本有什么区别?原子执行等于失败回滚吗?
🧑💻 面试官:Redis 事务执行到一半报错,会把前面的命令都回滚吗?
🙋♂️ 我:事务应该要么全成功,要么全失败。
🧑💻 面试官:先 SET 成功,再对这个字符串 LPOP 报错,那个 SET 会被撤回吗?
🙋♂️ 我:Redis 可能没有这种回滚。
🧑💻 面试官:那 Lua 的原子执行,也能直接理解成失败自动撤销吗?
Redis 里的“不被其他命令插进来”,不等于“失败自动撤销”。事务与脚本都要分清隔离执行、错误处理和持久化。
面试速答(60 秒版)
Redis 的 MULTI 把命令入队,EXEC 时按顺序集中执行,其间不会插入其他客户端的普通命令。但执行期某条命令报错,不会自动回滚此前成功的修改,其他排队命令仍可能继续执行。
WATCH 可以做乐观条件检查:被监视键在提交前发生相关变化时,EXEC 放弃这一批,应用再决定重试。它不是先拿锁,也不能无限重试。
Lua 适合把读取、判断、修改放到服务器端完成,避免客户端多次交互与中间插入。原子执行同样不等于运行时报错后自动回滚。
实际实现应尽量用现成原子命令,复杂逻辑先检查参数和类型,再修改;脚本要短,Cluster 下还要满足键访问规则。

知识点详解:事务与脚本到底保证了哪一部分
MULTI 是排队,EXEC 才执行
假设先发送 MULTI,再排入 SET、LPOP 和 INCR。收到 QUEUED 只表示入队,不是这一步已经修改成功;EXEC 会返回每条命令各自的结果。
若入队阶段就有错误,比如命令参数数量不对,现代 Redis 会拒绝执行这批事务。若执行阶段才发现类型不对,则是另一种情况:错误命令返回错误,其他命令仍会执行,之前成功的修改不会撤回。
所以检查 EXEC 返回列表时,不能只看“请求没断线”。一批结果可能同时有成功和错误。
WATCH 检查的是条件是否被别人改变
例如读出额度为 10,准备改成 9。普通客户端先读再写之间,另一客户端也可能改额度,产生覆盖。
WATCH 可以监视键,读出并计算,再通过 MULTI/EXEC 提交。如果提交前键被相关修改,EXEC 放弃,应用重新读取并计算。过期与淘汰等变化也要按版本规则考虑。
这属于乐观并发控制,不会让别人等着你。冲突频繁时重试成本高,应设置上限,并考虑服务器端判断或业务结构调整。
Lua 把判断和修改挪到同一段执行里
例如“额度大于零才扣一次”,可以在脚本中读取、校验再修改。其他普通命令不能在这段执行中间插入,就避免了客户端读写间隙。
不过脚本里的写入不是由一个可自动撤销的数据库事务包装。先写键 A,再遇到运行错误,并不意味着 A 自动恢复。
因此尽量在写入前完成可预知的参数和类型校验,并缩小修改边界。原子性回答的是观察和交错执行问题,不替你保证所有业务前置条件。
原子执行越久,其他请求等得越久
脚本是在服务器执行,不是提交给独立后台随意跑。长循环、巨大数据遍历和重计算,可能把其他请求挡住。
脚本缓存也不应被当成永久部署。重启、切换或缓存管理可能导致 EVALSHA 找不到脚本,应用要有合理加载策略;参数通过 KEYS、ARGV 传入,不为每个请求生成不同脚本文本。
Cluster 下,普通多键脚本仍受同槽与声明键等规则约束。执行成功还要与持久化、复制保障分开,不把“原子”扩展成“永不丢失”。
本题机制参考:Transactions、Lua 执行、Lua API。
面试官继续追问
Pipeline 能替代事务吗?
不能。它减少交互,不保证整批命令不被其他客户端穿插,也不提供事务条件检查。
Lua 报错之后能自动补偿吗?
Redis 不提供自动回滚此前脚本写入。补偿需要业务另行设计,最好先缩小脚本并前置校验。
只想给数字加一,要写 Lua 吗?
通常优先 INCR 等合适的单命令。已有原子命令更短、更容易维护,不必把简单操作升级成脚本。
面试速记卡
- MULTI:入队;EXEC:顺序执行并返回逐项结果。
- 错误:入队失败和执行报错不同。
- 回滚:执行期错误不自动撤销成功写入。
- WATCH:乐观检查,可冲突放弃,不是锁。
- Lua:短逻辑服务器执行,原子不等于自动回滚。
公司面试真题
真题根据求职者公开面经整理,题意经过概括,非逐字原话或公司官方题库;本文为 Sunday 的独立解析。
字节跳动 · 后端(抖音) · 社招
Redis 事务和 Pipeline 有什么区别?(题意整理)
社招一年半面经分享 · 抖音部分 ↗
历史面经,面试年份未明确;页面编辑于 2024-07-19