登录
首页 >  数据库 >  MySQL

【搞定面试官】系列:当面试官问你如何保证缓存和数据库双写一致性时,他到底想问什么

来源:SegmentFault

时间:2023-01-21 08:57:16 339浏览 收藏

小伙伴们有没有觉得学习数据库很有意思?有意思就对了!今天就给大家带来《【搞定面试官】系列:当面试官问你如何保证缓存和数据库双写一致性时,他到底想问什么》,以下内容将会涉及到MySQL、Redis、Java,若是在学习中对其中部分知识点有疑问,或许看了本文就能帮到你!

面试开始

小伙子你好,看你简历上写到了MySQL和Redis。今天我们就围绕他们两个展开吧。Redis和MySQL是后端开发中举重若轻的重要角色。实际开发中二者也基本上如影随行,为了提高性能和响应,Redis常常存放热点数据,MySQL存放所有数据,保证数据持久化。所以Redis可以说是MySQL的一部分数据。

那么问题来了,当MySQL中的持久化数据发生改变,如何通知Redis呢?也即如何保证缓存和数据库数据的双写一致性呢?


面试官您好,我们在开发中采取的方案是:先更新数据库,然后删除相应缓存,直到下次请求缓存发现没有数据,再从MySQL中读取,同时将数据更新到Redis

那为什么采用删除缓存而不是更新缓存呢?


如下图所示,如果采取更新缓存的方式,可能出现请求A先于请求B发生,更新缓存应该比请求B更新缓存早才对,但是因为网络等原因,B却比A更早更新了缓存,这就导致了脏数据

图片.png

其次,如果你是一个写数据库场景比较多,而读数据场景比较少的业务需求,采用更新缓存的方案就会导致数据压根还没被读取过,但缓存已被频繁的更新,浪费性能。

那先删除缓存,再更新数据库会不会有什么问题?


如下图所示,请求A进行写操作,删除缓存,请求B查询发现缓存不存在,就去数据库查询得到旧值,之后将旧值写入缓存,此时请求A再将新值写入数据库。
这种情况就会导致数据不一致的情形出现。而且,如果不采用给缓存设置过期时间策略,该缓存数据永远都是脏数据。

image.png

好的,刚刚说的方案确实在并发环境中都会有问题。那你们采用先更新数据库,再删除缓存,这种方案一定不会出现并发问题吗?


答案是不一定,也可能会出现并发问题。如下图所示,
当步骤Step3的更新数据库操作比步骤Step2的读取数据库操作耗时更短,就有可能使得步骤Step4先于步骤Step5,此时缓存中的就是脏数据。但一般情况下数据库的读操作的速度是远快于写操作的(此点从MySQL的并发读写量就可看出,相同硬件配置下并发读的效率是并发写的数倍)

图片.png
因此,如果你想实现基础的缓存和数据库双写一致的逻辑,那么在大多数情况下,在不想做过多设计、增加太大工作量的情况下,请
先更新数据库,再删除缓存!

先更新数据库,再删除缓存,除了你刚刚说得问题,还会有别的问题吗?


如果MySQL采用了读写分离架构,当请求A更新数据在master库上并删除了缓存,但此时数据库主从同步还未完成。请求B查询缓存发生Cache miss之后从slave库上读取到的还是旧值,此时也会造成数据不一致。

图片.png

你刚刚说到先更新数据库,再删除缓存也有可能造成数据不一致,怎么解决呢?


采用延时双删。如下图所示,请求A更新数据库之后,为防止删除缓存先行发生于请求B的将缓存写入旧值,可以通过将请求A更新完数据库之后休眠一会(例如100ms,200ms,根据实际业务场景拟定),再删除缓存,这样基本能保证缓存中存放的不会是脏数据。主从架构也是这个原理,就是请求A在更新master之后不用立即删除缓存,通过延时双删保证主从同步已经完成,最后删除缓存数据。

图片.png

但你这种方案,请求A休眠一段时间的话,可能会影响到接口的RT,降低系统的吞吐量,如何解决呢?


这里比较优雅的方案是通过异步实现。即开启一个线程池,在请求A的时候开启一个单独的线程,异步的休眠一段时间然后执行缓存删除。当然也可以通过将缓存中相应的key扔到消息队列,通过MQ异步删除,但仅为了异步删除缓存就多加了一层消息队列,可能会造成系统设计更加复杂,并且会带来别的问题。

前面一直有提到删除缓存,如果删除缓存失败了怎么办呢?


再加一个重试机制,保证删除缓存成功。

如果我一定要数据库和缓存数据一致性怎么办?


没有办法做到绝对的一致性,这是由CAP理论决定的,缓存系统适用的场景就是非强一致性的场景,所以它属于CAP中的AP。

CAP理论是分布式系统中的经典理论,即一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。

根据BASE(Basically Available,Soft State和Eventually Consistent)理论,缓存和数据库只能做到数据的最终一致性

面试结束

时间也不早了,今天就到这里吧,看出来小伙子对这块掌握的比较深入。我们公司就缺少你这种人才,要不现在就签把Offer签了吧。
这个时候你肯定欲绝还迎,一手接Offer一边摆摆手:不行不行,深圳马那边也急着等我给回复呢,催了我好几天了。
面试官一听,payroll组何在,加价!

小结

使用缓存并不是一个很简单的事情,尤其在需要缓存与数据库保持强一致的场景,才知道让数据库数据和缓存数据保持一致性是一门很高深的学问。
从远古的硬件缓存,操作系统缓存开始,缓存就是一门独特的学问。这个问题也被业界探讨了非常久,争论至今也无果,因为其实这是一个权衡的问题。
我是少侠露飞,爱技术,爱分享。

本篇关于《【搞定面试官】系列:当面试官问你如何保证缓存和数据库双写一致性时,他到底想问什么》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于数据库的相关知识,请关注golang学习网公众号!

声明:本文转载于:SegmentFault 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>
评论列表