面试官:如何避免缓存脏读

进入电商公司以后,缓存用的实在太多了。特别是最近做的项目,整个数据都是直接从其他电商平台拉取的,全部放在Redis中。

想起了去年跳槽时众多面试的一场,面试官问我如何避免缓存脏读问题:

假设某个数据缓存在Redis中,当我在后台更新了数据后,如何保证客户端读取的是最新的数据呢?
问题描述:什么是缓存脏读

首先搞懂这个问题是什么意思。

eec06e2c22834f5f9ea9a4de0edc6779.png
由于设置了缓存失效时间(比如1小时),那么在这一小时内,如果有人更新了DB的数据,只要缓存不失效,Redis就不会主动读取DB并更新数据,那么用户看到的其实都是旧的数据,与DB不一致,此谓缓存脏读。 

我的解决方案

我当时的回答是:找到更新数据的入口,每次更新DB都同步或异步更新缓存。比如

public void update(){
  1.更新数据库
  2.更新缓存(同步或异步)
}
面试官紧接着问我,如果A线程刚更新完缓存,B线程又来更新数据库,且尚未更新缓存,此时C线程从缓存中读到的就是旧的数据了。

26d878c43ad54658b3d528cfa4fa4e80.png
我当时也是年轻啊,直接说:加锁,把1和2设置为原子操作。 

实际上,加锁也是解决不了问题的,因为Redis和DB又不是同一个连接,不在一个事务里…

面试官的建议

当然,我是不服气的,于是我承认自己不知道,并问面试官有什么高见。面试官慢悠悠地说道:

public void update(){
  0.更新缓存(同步或异步)
  1.更新数据库
  2.更新缓存(同步或异步)
}
反思:缓存脏读无法避免,也没必要避免

面试官推荐的写法这确实能在一定程度上保证“缓存里的一定是最新数据”,但反过来却无法保证“DB是最新的数据”...而且万一DB更新那一步挂了呢,岂不是缓存是最新的,DB却是旧的?

哎,有时候面试就是要自信点,不能被面试官唬住,有些问题确实就是没有十全十美的解决方案,总是要舍弃一些,获取一些。
评论区有好多知友留言讨论,很有启发。关于到底缓存和DB能不能做强一致,我也抛一个自己的观点,然后大家在提出自己的见解前,看看能不能解决我的问题。

先不谈论缓存和DB的强一致,我们先看多个DB如何保持强一致。根据CAP理论,一致性、可用性和分区容错性至多只能满足两个,通常是CP或者AP。对于CP来说,为了强一致性,一般牺牲部分可用性。比如对于多个DB节点,要想保证数据强一致,那么在数据同步完毕之前,应该阻塞所有的读(效率大打折扣,实际开发往往追求最终一致性即可)。

8d10c93cc7d14e4a8b55c7e48310fed0.png
同样的,把DB2换成Redis,要想保证数据强一致,就要做到“DB更新时,阻塞缓存读”,这会强行把Redis的效率拉低到和DB一个水平,而这个就违背了使用Redis的初衷。 

至于评论区提到的,把更新缓存改为删除缓存,这样有线程访问缓存时,如果发现无数据就会去读最新的DB数据,似乎挺合理的,但本质上和更新的做法一样,无法避免缓存脏读,而且还要考虑缓存击穿问题,会让问题复杂化。

其实啊,你会发现这个问题一直扯皮下去是没有意义的。我前脚刚读取数据,后脚就被人改了,这个本来就是合理的呀。即使是单DB,你也不能保证此刻读完数据,下一刻这个数据不会被修改吧?再退一步,如果用户读取完数据,一直停留在页面上没操作,半小时后才点提交,此时数据肯定和数据库不一致呀,这个又怎么解决呢?没法解决啊...它就是正常的一个操作而已,做好数据校验和幂等,提示用户操作失败即可。

所以,缓存脏读问题本质是展示不一致的问题。

缓存脏读只要不是特别严重(一小时都没更新,影响数据展示),都是可接受的,没太大必要考虑什么强一致。比如更新DB时同步/异步刷新缓存,既简单又经济,几乎是毫秒级别,足够了。

你看看,知乎的点赞数据,文章列表看到的点赞数和文章详情的点赞数往往是不一致的,但也无伤大雅…是我们把不一致看得太重要,甚至害怕不一致。

不过,最最重要的还是一定要给缓存设置过期时间,保证至少还有缓存失效来兜底刷新(不设置过期时间不仅影响展示,还有个很严重的问题是,随着时间推移,有些数据很久不再被访问,却死赖在缓存里,占着茅坑不拉屎)。

所以,结论是:

缓存脏读是无法避免的,或者避免的代价太高了,本身“避免缓存脏读”就是一个伪命题,要求强一致的就不该放在缓存里。建议在此觉悟上,采取更新DB时刷新缓存的策略,保证毫秒级别的更新即可。f1d1574f9c434e49881d5dd5ded72097.gif

 

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蜀州凯哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值