Skip to content

P50 面试重灾区:Redis 事务与原子性的大坑 ​

面试题:Redis 事务(MULTI/EXEC)支持原子性吗?和 MySQL 事务有什么区别?

1. Redis 事务是什么 ​

text
MULTI    -- 开启事务
SET k1 v1
INCR k2
EXEC     -- 一起执行

事务期间命令排队,EXEC 时按顺序执行。特点:

  • 命令依次执行,不会被其他客户端命令插入(隔离性);
  • 一条命令失败不影响其他命令执行(没有回滚!);
  • Redis 事务是弱事务,和 MySQL 事务的 ACID 差别很大。

2. 大坑一:不支持回滚 ​

  • 运行时错误(如对字符串做 INCR)不会让事务回滚,其他命令照常执行;
  • 只有入队时的语法错误会让整个事务失败(Redis 2.6.5+ 提前报错);
  • 所以"原子性"指要么全不执行(语法错误/被 DISCARD),要么一条条执行完,不等于"要么全部成功要么全部失败"。

3. 大坑二:WATCH 才是乐观锁 ​

text
WATCH key    -- 监视 key
MULTI
...命令...
EXEC
  • WATCH 监视的 key 在事务执行前被其他客户端修改 → 事务被取消(返回 nil),实现"乐观锁"效果;
  • 应用需要重试;
  • 不 WATCH 的话,事务里是"先读后写"就会踩到并发覆盖问题。

4. 大坑三:Lua 脚本才是真正的原子操作 ​

实际生产中要"判断 + 修改"原子执行(如扣库存),别用 MULTI/EXEC,用 Lua 脚本:

lua
-- Redis 会原子执行整个脚本(EVAL),期间不处理其他命令
if tonumber(redis.call('get', KEYS[1])) > 0 then
    return redis.call('decr', KEYS[1])
end
return -1

Redis 单线程执行模型下,Lua 脚本天然原子。

5. 面试对比表 ​

特性Redis 事务MySQL 事务
隔离命令不被插入(单线程排队)MVCC + 锁
回滚无有(undo log)
持久性取决于 AOF 策略redo log + 刷盘
并发控制WATCH 乐观锁锁/MVCC

一句话总结 ​

Redis 事务(MULTI/EXEC)只是"命令排队依次执行",不支持回滚、不做原子性保证,真正的原子操作要用 Lua 脚本,防并发覆盖要用 WATCH 或分布式锁;别把 Redis 事务当 MySQL 事务用。

基于 VitePress 重建