Redis 缓存一致性到底怎么做?90% 的人第一步就错了

44 阅读5分钟



大家好,我是 31 岁、爱折腾技术、也爱讲故事的小米

有一天,我在楼下便利店买水,老板一边扫码一边叹气。

我问他:“咋了老板?”

老板说:“我这店里有两个账本,一个是货架上的库存牌子,一个是电脑里的真实库存

有时候货都卖光了,牌子上还写着‘还有 3 瓶’,客人骂我骗人;有时候牌子没更新,明明有货,客人又走了。”

我当场就笑了。这不就是我们天天在系统里折腾的那点事吗?

  • 数据库:电脑里的真实库存
  • Redis 缓存:货架上那块“库存展示牌”

你只要用了缓存,你只要缓存 + 数据库双写,那你就一定会遇到一个灵魂拷问

数据一致性问题,怎么搞?

而且,这道题,java 社招面试几乎必问。今天这篇文章,我们就从这个“便利店”的故事,彻底把这道题讲透。

先把结论说清楚:为什么双写一定会不一致?

在讲方案之前,小米必须先泼你一盆冷水。

只要是「缓存 + 数据库」双存储

你面对的真实世界是这样的:

  • 网络可能超时
  • 线程可能被切走
  • JVM 可能 GC
  • Redis 可能刚好重启
  • 数据库可能慢了一拍

所以结论只有一个:强一致性,在缓存架构下,几乎不可能

你能做的,只有两件事:

  • 要么 牺牲性能,换一致性
  • 要么 牺牲极小概率一致性,换性能

面试官真正想问的,从来不是“有没有完美方案”,而是:你知道自己在牺牲什么吗?

第一种方案:读写请求串行化(最硬核、最反人类)

1、类比一下:一个人记账

回到便利店的故事。

老板说:“那我以后不让员工自己记账了,所有卖货、进货,都排队找我一个人登记。”

结果是什么?

  • 绝对不会出错
  • 顾客排队,骂街

2、技术方案本质

这就是面试官常说的:读请求和写请求串行化,串到一个内存队列里

示意图是这样的:

3、为什么它一定一致?

因为它满足了两个条件:

  • 所有操作 顺序执行
  • 不存在并发写、并发读穿插

换句话说:时间线被你“锁死”了

4、伪代码示意

5、面试官追问点(非常关键)

那它的问题是什么?吞吐量直接腰斩,甚至更惨。

  • 本来 8 核 CPU
  • 你硬生生用成了 1 核
  • 想扛住流量?多上几倍机器

6、真实结论

实话实说:这方案存在的最大意义,是用来“面试吹牛”和“对比其他方案”的。

第二种现实方案:允许极小概率不一致(主流)

讲到这里,我们回到现实。

便利店老板想了想,说:“算了,牌子偶尔错一次没事,但不能天天排长队。”

这,就是互联网系统的真实选择

经典方案:先更新数据库,再删除缓存(重点!)

1、为什么是“删除缓存”,而不是“更新缓存”?

这是面试官最喜欢挖坑的地方。很多新手会写:

看起来很合理,对吧?但这是个坑。

2、并发下的致命问题

假设:

  • 线程 A 更新数据
  • 线程 B 正在读数据

时间线如下:

结果:缓存里是旧数据。

3、正确姿势:先 DB,再删 Cache

4、为什么这样更安全?

因为:

  • 删除缓存是 幂等操作
  • 读请求会触发 缓存重建
  • 最坏情况:短时间读到旧值

但不会长期脏数据。

那为什么还会不一致?面试官继续追问

你以为到这就结束了?不,面试官会继续问你:如果 删除缓存失败 呢?

这时候,提醒你一句:这道题的精髓,不在代码,而在“系统设计思维”

删除缓存失败的解决思路(思路比方案重要)

1、重试机制

2、延迟双删(面试加分项)

  • 第一次删,防止并发读
  • 第二次删,兜底

3、消息队列补偿(高级)

  • 更新 DB 成功
  • 发送 MQ 消息
  • 异步消费删除缓存
  • 失败可重试

三种方案横向对比(一定要记)

这是面试必备表格,建议你背下来。

面试时的标准回答模板(直接背)

如果面试官问你:

Redis 如何保证缓存与数据库双写时的数据一致性?

你可以这样答:

“只要使用缓存,就不可避免会涉及缓存和数据库双写,只要双写,就一定存在一致性问题。

如果业务要求强一致性,可以通过读写请求串行化的方式,把所有请求串到内存队列中,但代价是吞吐量大幅下降,通常不推荐。

在绝大多数互联网场景中,采用的是最终一致性方案,即先更新数据库,再删除缓存。

即使在极端并发情况下,最多也只是短时间内读到旧数据,发生概率非常低,可以通过延迟双删、重试或消息队列进一步兜底。”

这一段,说完,面试官基本不会再追。

总结:技术和人生,其实一样

最后,回到便利店老板。他跟我说:“有时候牌子错一会儿没事,但我得保证,最终是对的。”

我突然觉得,这和我们做系统、做人,其实一模一样。

  • 完美,代价极高
  • 容错,才是常态
  • 你要清楚,你选择牺牲什么

缓存一致性这道题,从来考的不是 Redis,而是你对 系统权衡能力的理解

END

如果你看到这里,说明你已经把这道社招高频题,彻底拿下了。

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!

好朋友们,我们下篇见。