Sunday面试指南

Redis 事务和 Lua 脚本有什么区别?原子执行等于失败回滚吗?

🧑‍💻 面试官:Redis 事务执行到一半报错,会把前面的命令都回滚吗?

🙋‍♂️ 我:事务应该要么全成功,要么全失败。

🧑‍💻 面试官:先 SET 成功,再对这个字符串 LPOP 报错,那个 SET 会被撤回吗?

🙋‍♂️ 我:Redis 可能没有这种回滚。

🧑‍💻 面试官:那 Lua 的原子执行,也能直接理解成失败自动撤销吗?

Redis 里的“不被其他命令插进来”,不等于“失败自动撤销”。事务与脚本都要分清隔离执行、错误处理和持久化。

面试速答(60 秒版)

Redis 的 MULTI 把命令入队,EXEC 时按顺序集中执行,其间不会插入其他客户端的普通命令。但执行期某条命令报错,不会自动回滚此前成功的修改,其他排队命令仍可能继续执行。

WATCH 可以做乐观条件检查:被监视键在提交前发生相关变化时,EXEC 放弃这一批,应用再决定重试。它不是先拿锁,也不能无限重试。

Lua 适合把读取、判断、修改放到服务器端完成,避免客户端多次交互与中间插入。原子执行同样不等于运行时报错后自动回滚。

实际实现应尽量用现成原子命令,复杂逻辑先检查参数和类型,再修改;脚本要短,Cluster 下还要满足键访问规则。

事务执行期错误不会自动回滚成功写入,Lua 原子执行也不提供自动撤销

知识点详解:事务与脚本到底保证了哪一部分

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 的独立解析。

浏览公司面试真题 →
简历汪永久免费在线制作简历,模板直接套用、导出无水印,永久免费、下载免费,不需要付费解锁任何功能。去写简历