一、Redis 事务基础类
Q1:Redis 事务是什么?
一组命令的集合,用
MULTI开启,EXEC执行。命令入队,
EXEC时顺序、一次性执行。执行期间不会被其他客户端命令打断。
但不支持回滚,是“弱事务”。
Q2:Redis 事务的命令有哪些?
Q3:Redis 事务的执行流程?
MULTI开启事务。命令入队,返回
QUEUED。EXEC执行所有入队命令。返回结果数组,每个命令一个结果。
DISCARD可放弃,清空队列。
Q4:Redis 事务支持回滚吗?
不支持。
如果某条命令运行时出错,其他命令仍会执行。
只有入队时语法错误,
EXEC才整体失败。所以 Redis 事务不保证原子性(指回滚意义上的)。
Q5:Redis 事务和数据库事务的区别?
Q6:Redis 事务的错误分几种?
入队错误:命令不存在或语法错,
EXEC时整体失败。执行错误:如对 String 执行
LPUSH,该命令失败,其他继续。Redis 不因执行错误回滚。
Q7:为什么 Redis 事务不支持回滚?
设计哲学:错误是编程错误,应在开发时发现。
回滚需要额外记录和开销,影响性能。
单线程执行,不存在并发导致的中间状态。
所以选择简单高效,不回滚。
二、WATCH 与乐观锁类
Q8:WATCH 是什么?
乐观锁机制。
WATCH key后,如果 key 在EXEC前被修改,事务失败。EXEC返回 nil,需重试。用于实现 CAS(Check-And-Set)。
Q9:WATCH 的工作原理?
WATCH key记录 key 的版本。其他客户端修改 key,标记该 key 被修改。
EXEC时检查被 WATCH 的 key 是否被改。被改则事务不执行,返回 nil。
Q10:WATCH 怎么实现乐观锁?
WATCH stock
val = GET stock
if val > 0:
MULTI
DECR stock
EXEC
else:
UNWATCH如果 stock 被改,
EXEC失败,重试。适合冲突少的场景。
Q11:WATCH 和悲观锁的区别?
Q12:WATCH 的 key 在 EXEC 前过期会怎样?
视为被修改,事务失败。
因为过期也是一种变更。
需重试。
Q13:UNWATCH 什么时候用?
放弃事务前取消监视。
EXEC或DISCARD后自动 UNWATCH。手动 UNWATCH 可提前释放。
三、Lua 脚本类
Q14:Redis 为什么引入 Lua?
事务不支持回滚,复杂逻辑难保证原子。
Lua 脚本在 Redis 中原子执行,不被其他命令打断。
可封装复杂逻辑,减少网络往返。
适合 CAS、限流、分布式锁等。
Q15:Lua 脚本的原子性怎么保证?
Redis 单线程执行命令。
Lua 脚本作为一个整体执行,期间不处理其他命令。
所以脚本内所有操作原子。
但脚本执行时间长会阻塞其他请求。
Q16:Lua 脚本怎么执行?
EVAL script numkeys key... arg...EVALSHA sha1 numkeys key... arg...:用缓存脚本。SCRIPT LOAD:预加载脚本,返回 SHA1。SCRIPT EXISTS、SCRIPT FLUSH:管理脚本缓存。
Q17:EVAL 和 EVALSHA 的区别?
EVAL:每次传完整脚本,网络开销大。EVALSHA:传 SHA1,脚本已缓存,开销小。生产用
SCRIPT LOAD+EVALSHA。脚本不存在时回退
EVAL。
Q18:Lua 脚本能保证原子性吗?
能,脚本执行期间不被打断。
但不保证“要么全成功要么全失败”。
脚本内错误不会自动回滚已执行的命令。
原子性指“不被中断”,不是“可回滚”。
Q19:Lua 脚本有什么注意事项?
脚本要短,避免阻塞。
不要用随机命令(除非固定 seed)。
不要用阻塞命令。
key 要显式传,不要硬编码。
集群下所有 key 要同槽。
Q20:Lua 脚本会阻塞 Redis 吗?
会。脚本执行期间单线程被占用。
长脚本阻塞所有请求。
要控制执行时间,避免死循环。
lua-time-limit可设超时,但不会自动杀。
Q21:Lua 脚本怎么访问 Redis 数据?
redis.call('get', KEYS[1]):执行命令,出错抛异常。redis.pcall(...):执行命令,出错返回错误表。KEYS、ARGV是传入的 key 和参数。
Q22:Lua 脚本能做分布式锁吗?
能。删锁的校验 + 删除用 Lua 保证原子。
例:
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end这是 Lua 最经典的应用。
四、Pipeline 与事务对比类
Q23:Pipeline 和事务的区别?
Q24:Pipeline 是原子的吗?
不是。命令之间可能插入其他客户端命令。
只是批量发送,减少网络往返。
要原子用事务或 Lua。
Q25:事务和 Lua 怎么选?
简单命令组合:事务。
复杂逻辑、需原子:Lua。
需条件判断:Lua。
需回滚:都不支持,用数据库。
Q26:Pipeline + 事务能一起用吗?
能。
MULTI、命令、EXEC一起打包发送。减少 RTT,同时保证事务。
但注意批量大小。
Q27:Lua 和事务的原子性区别?
事务:命令入队,
EXEC时顺序执行,不中断。Lua:脚本整体执行,不中断,可写复杂逻辑。
两者都不支持回滚。
Lua 更灵活,事务更简单。
五、实战与陷阱类
Q28:事务中命令入队后能改吗?
不能。入队后只能
EXEC或DISCARD。想改要
DISCARD重新来。或用 WATCH + 重试。
Q29:WATCH 的 key 很多会怎样?
记录多个 key 版本,开销增加。
EXEC时检查所有 key。尽量少 WATCH。
Q30:Lua 脚本中能用随机数吗?
能,但要用固定 seed,保证可重现。
Redis 5+ 要求脚本确定性。
否则主从/AOF 可能不一致。
Q31:Lua 脚本在集群下有什么限制?
所有 key 必须在同一槽。
用 hash tag
{}保证同槽。不能跨槽访问。
Q32:脚本缓存会占内存吗?
会。脚本存在内存中。
用
SCRIPT FLUSH清理。大量不同脚本要管理。
Q33:Lua 脚本超时了怎么办?
lua-time-limit默认 5 秒。超时后 Redis 记录日志,但不自动杀。
其他客户端收到 BUSY 错误。
只能
SCRIPT KILL(未写)或SHUTDOWN NOSAVE。
Q34:Redis 事务能保证隔离性吗?
能。单线程执行,事务期间不执行其他命令。
天然串行隔离。
但 WATCH 失败是乐观锁,不是隔离级别。
Q35:为什么 Lua 脚本要显式传 key?
集群下 Redis 要知道 key 路由到哪个槽。
显式传 key 才能正确路由。
不要从 ARGV 拼 key。
评论