DC娱乐网

Redis 事务其实没你想的强?深扒它的三个致命短板

大家好,我是 31 岁爱讲故事的小米,一个喜欢在通勤路上刷源码、在深夜里喝咖啡写技术文章的大哥哥。前两天去面一家公司,



大家好,我是 31 岁爱讲故事的小米,一个喜欢在通勤路上刷源码、在深夜里喝咖啡写技术文章的大哥哥。

前两天去面一家公司,本来以为是常规“扯扯项目、问问框架、聊聊八股”那种,却没想到面试官直接用一句:

“小米,聊聊事务吧。ACID 是什么?说具体点。”

我当场一个激灵:这不是八股中的八股吗?但面试嘛,不能只说术语,要讲人话,还要讲故事。

于是我端起喝了一半的美式,就像讲武侠一样,把“事务”讲成一出江湖大戏。

这一讲,面试官听得直点头,最后还说一句:“有意思,你这个解释方式我第一次听。”

所以今天,就把那套讲故事的方式写下来,给你做武功秘籍,不仅让你理解,还能让你在下一次面试时吊打法器。

第一章:ACID 是什么?江湖规矩四大门派

我当时对面试官笑笑说:

“事务嘛?就像江湖中的‘四大门派’,想维护世界和平就必须守住四个规矩。”

这四个规矩就是:

A - Atomicity 原子性

C - Consistency 一致性

I - Isolation 隔离性

D - Durability 持久性

为了让他听懂,我开始讲故事:

原子性:一刀到底,不分你我

原子性像是一个江湖杀手的规矩:

要么成功一刀毙命,要么收刀走人,不能砍一半卡住。

事务也是一样:

要么全部执行成功

要么全部不执行

比如银行转账:

A 扣 100 元

B 加 100 元

这两件事必须成对出现。如果只扣不加?社会乱套了。

因此数据库保证在任何异常情况下都能“自动回滚”,确保事务像一刀到底的武功。

一致性:江湖必须保持平衡

一致性像江湖的天道平衡:

不管发生什么,一定要保持前后逻辑一致。

比如银行转账前:

总金额 = 1 万

转账后:

总金额仍旧 = 1 万

不会凭空多钱,也不会凭空少钱。

数据库通过约束、触发器、外键、校验等保证这个平衡。

隔离性:各练各的功,不互相干扰

我当场举例:

假设你去银行取钱,同时我也去银行取钱。你在窗口操作,我在 App 上操作,我们两个的事务不能互相干扰,否则肯定会出现:

你看到的余额被我修改

我看到的余额被你覆盖

隔离性就是:

让每个事务拥有独立练功房间,不被其他事务干扰。

数据库有 4 个隔离级别:

Read Uncommitted

Read Committed

Repeatable Read

Serializable

隔离性越高,越安全,但性能越低。这就是江湖中典型的“安全 vs 性能”两难。

持久性:功练成了,谁也抹不掉

持久性就像练成了绝世神功,无论外界怎么风雨飘摇,你的功力不会掉。

数据库里,事务只要提交成功:

数据必须持久化

即使断电、宕机、服务器崩溃也不能丢

这就是 WAL 日志、redo log、binlog、checkpoint 等的价值。

到这里,面试官笑着点点头:“不错,有画面感。”

第二章:Redis 事务?呵呵,它不姓 ACID,它姓 KISS(简单粗暴)

Redis 为什么火?就是因为它足够 简单 + 暴力 + 快。于是它在事务这件事上,也延续了“简单到离谱”的路线。

我继续讲故事:

“Redis 的事务不像数据库那样是‘江湖大师’,更像街边撸串小店:

要不要一次性把菜都烤完?行。

要不要帮你回滚?不行。

要不要隔离?嗯……我们这里没那玩意。”

面试官:“继续说。”

于是我说:

第三章:Redis 支持隔离性吗?不支持!

Redis 用的是单线程模型。

也就是说,一个事务在执行的时候:

Redis 没法让其他命令插队,但也没法提供真正的隔离级别。

细说:

Redis 的事务只是把命令收集起来

等到 EXEC 时,一次性按顺序执行

但整个过程中,Redis 不会隔离读写,不会快照,不会锁住数据

所以 Redis 没有隔离性!

你甚至在事务执行前:

看到的数据已经被别人改了

watch 的键被修改后,整个事务直接失败

典型的“你别动,我做完再说”的模式。

第四章:Redis 事务保证原子性吗?不保证!

这句我一说完,面试官立刻坐直。

因为很多人以为 Redis 的事务很强,其实不是。Redis 的事务是这样:

Redis 只保证 EXEC 中的命令会连续执行,不会被其他客户端插队

但不保证全部成功,要失败就会局部失败

比如你执行:

结果:

SET a 成功

INCR b 报错

SET c 成功

Redis 不会帮你回滚!不会!不会!不会!

这就是 Redis 事务最大的坑:它不具备原子性,只具备“顺序性”。

第五章:Redis 支持回滚吗?不支持!

我给面试官举例:

“数据库像是安全顾问,做错事会帮你撤销。

Redis 像是你那粗线条的好兄弟:

哥们我帮你做了,但做错我也没办法。”

Redis 的哲学:

错误命令不会导致整个事务失败

成功的命令不会被撤销

Redis 从来不保证回滚

为什么?因为 Redis 追求速度,回滚会拖慢性能。

第六章:那 Redis 事务有什么用?

这也是很多面试官爱问的关键点。

Redis 事务适合:

批量执行命令

给多个命令增加“一起执行”的语义

配合 WATCH 实现轻量级乐观锁

保证不会被插队(单线程)

但绝不适合:

强一致

强隔离

强原子性

金融系统、转账系统

第七章:Redis 事务的三种核心机制

面试官问:“那 Redis 事务到底有哪些实现方式?”

我直接给他画三条线:

1、MULTI / EXEC / DISCARD —— Redis 原生事务机制

流程:

WATCH key(可选)——监听变化,相当于乐观锁

MULTI —— 开启事务

一堆命令排队

EXEC —— 执行

DISCARD —— 放弃事务

缺点:

没有回滚

没有隔离

执行出错不会撤销之前命令

2、WATCH —— 乐观锁

WATCH 就是:

“我盯着某几个键,你要是敢改,我就直接放弃执行。”

适合解决:

抢购

库存扣减

CAS 更新(Compare And Set)

但 WATCH 失败后要自己重试。Redis 不会帮你。

3、Lua 脚本 —— 真正的“Redis 原子性之王”

我当时看着面试官说:

“如果你真的想在 Redis 中实现原子性,那你一定要用 Lua。”

因为:

Lua 脚本在 Redis 里是原子执行

期间不会有其他命令插队

执行要么全部成功,要么全部失败(逻辑自己写)

可以用逻辑手动模拟回滚

Lua 才是 Redis 中真正的“事务神器”。

面试官直接点头:“对,这点很重要。”

第八章:总结——你在面试中应该这样说

为了让你面试不翻车,这里给你一段 最佳答案:

ACID:

原子性:事务要么全部成功,要么全部失败

一致性:数据前后符合规则

隔离性:多个事务互相隔离

持久性:提交后的数据不能丢失

Redis 事务:

Redis 不支持严格意义上的隔离性

Redis 事务不保证原子性,执行中有命令失败不会回滚

Redis 事务不支持回滚

Redis 事务机制主要包括:

MULTI / EXEC

WATCH(乐观锁)

Lua 脚本(唯一能保证原子性)

END

这就是我那天在面试现场讲的一套故事型解释方式。

面试官很满意,因为:

不只是背概念

而是真正解释了背后逻辑

还能用故事让面试官不困

最重要——你讲得和别人不一样

希望你在下一次面试时,也能把事务讲得像江湖一样精彩。