两次被裁之后,我终于解决了数据库缓存一致性问题
一
我是一名毕业两年的程序员。
算上实习,工作三年了,正是一个程序员的黄金时代,这让我普通而自信。
但是从实习期,我就被辞退两次了。
今天是我的又一场面试,而且是大厂面试。我要一洗前耻,证明自己。
好了,我要赶紧出发,不然通往美好生活的996路公交车又堵了。
经历过西二旗的人潮人涌之后,我按时来到了面试官面前。
和面试官寒暄了几句,他直接问了一个技术问题
"如果网站流量太高,我们通常会加缓存来减轻数据库压力,读缓存很简单,如下图
关于写缓存,你知道怎么设计这个方案,保证缓存与数据库的数据一致性吗?"
一刹那,往事涌上心头,因为我在这个问题上,已经栽了两次了。
二
第一次是在实习期。
那年二十,刚刚工作,每日如喽啰。
实习的公司是一家外包公司,没有什么技术规范,按时上线是大家最重要的诺言。
我的第一个任务就是增加缓存,降低Mysql的压力。
这个任务最核心的就是写缓存时怎么保证缓存和数据库的一致性,当年还是实习生的我显然没有意识到这个需求的复杂性,直接采用的方案就是
先更新数据库,再更新缓存
上线第二天,网站就出了Bug,我就被甲方爸爸投诉了。
后来,在复盘中我才发现,网站挂了的原因是:
如果同时有请求A和请求B进行更新操作,那么会出现
请求B是最后请求的,那么应该是他最后更新缓存为正确的数据,但是有可能请求A处理的更慢,所以请求A更新了最后的缓存。
另外这个系统写数据库场景比较多,而读请求比较少,这种方案就导致数据压根还没读到,缓存就被频繁的更新,浪费性能。
然后我当天就被辞退了,理由是在办公室工位吃螺蛳粉。
三
没想到啊没想到,在这个问题上,我还能梅开二度。
毕业之后进入的第一家公司,兢兢业业两年半,业务量也逐渐上来了。
访问量上升,代表着我的薪水也有机会上升。我立马做了个方案,准备在一向不看好我的经理面前表现一把。
核心逻辑就是采用
先删缓存,再更新数据库
经理看完方案,直接画了下面一个图
淡淡说道:"这样的话,缓存都是脏数据了。"
我想了下,说:"确实,不过可以双删缓存。"
public void write(String key,Object data){
redis.delKey(key); // 删缓存
db.updateData(data); //更新数据库
Thread.sleep(1000); // 根据业务执行时间确定具体的时间
redis.delKey(key); // 我再删缓存
}
经理笑了笑,说:"我们可是采用了MySQL读写分离架构啊,如果有下面这样两个请求
还是会导致数据不一致啊!"
我有点不悦,这可是我主动加班做的方案,一句赞赏都没有,怎么老是被质疑?
但还是回答道 "那就sleep时间修改为业务执行时间
+主从同步时间
就可以了,就是等主从同步完了再删一次。"
经理又问道:"嗯,可以。不过你这样删除缓存两次,会造成吞吐量降低,怎么办?"
我觉得很不爽了,又不是他开发。
算了,我回应到:"那就将第二次删除改为异步的。即重新起一个线程,异步删除。"
经理又问道:"那要是第二次删除缓存失败呢?"
我无奈了:"您说呢,毕竟您也是经理,要不您也说两个方案让我学习一下?"
经理笑了笑:"年轻人,路走窄了啊!"
然后我当天就被辞退了,理由是我代码缩进用的Tab而非空格。
四
此刻,我坐在面试官前面,面对这个问题已经有三年了,我也早已胸有成竹。
想到这,我直接站了起来,走到白板面前,说道:"实不相瞒,前两次我的离职都和这个问题有关,所以我也思考了很多,不如直接我就讲讲最优解法。那就是
先更新数据库,再删缓存
当然,这样也会有并发问题,比如
但是数据库读操作速度远快于写操作,所以存在脏数据的可能性为0。
当然如果您问,如果真的存在怎么办?
简单,双删就行了,即第一次删除缓存之后,等待一段时间重新再删一次。
当然您如果还问,删除缓存失败了怎么办,解决方法如下
即引入消息队列,删除缓存失败的记录下来重复删除,直到成功方可。如此一来,万无一失。"
面试官点了点头,鼓了鼓掌,叹到:"优秀,不过我好奇你前两次的离职,可以和我讲讲吗?"
面试官听完我的经历之后,对我深表同情,问道:"假如我给了你offer,你走到之前那些开除你的人面前,你会说什么呢?"
我走到窗户旁边,望向远方,轻声道:"我会走到他们面前,把offer甩给他们看,告诉他们,我等了三年,就是要等一个机会,我要争一口气,不是想证明我了不起,我只是要告诉人家,我失去的东西一定要拿回来!
— 【 THE END 】— 本公众号全部博文已整理成一个目录,请在公众号里回复「m」获取! 3T技术资源大放送!包括但不限于:Java、C/C++,Linux,Python,大数据,人工智能等等。在公众号内回复「1024」,即可免费获取!!