面试必问的缓存使用:如何保证数据一致性、缓存设计模式

程序员囧辉

共 2908字,需浏览 6分钟

 · 2021-03-15


前言


缓存使用在现在的项目中非常常见,缓存在为我们带来便利的同时,也会带来一些常见的问题,如果不谨慎使用,可能会带来意想不到的结果。


面试中,缓存使用带来的各种问题也是面试官喜欢考察的点,今天我将跟大家一起探讨以下几个常见的问题:

如何保证数据库和缓存的数据一致性

先操作数据库 or 先操作缓存?

失效缓存 or 更新缓存?

缓存的常见设计模式有哪些?



正文


缓存查询通用流程



这个缓存查询流程相信大家都不陌生,这应该是目前应用最广的缓存查询流程。


但是大部分人可能不知道,这个流程其实有一个名字:Cache Aside Pattern,这是缓存设计模式的一种。


上图是 Cache Aside Pattern 的查询流程,而更新流程如下。



这个更新流程会引出两个问题:


1)为什么是先操作数据库,可以先操作缓存吗?


2)为什么是失效缓存,可以更新缓存吗?


接下来我们一一分析。



先操作数据库 or 先操作缓存


先操作数据库

案例如下,有两个并发的请求,一个写请求,一个读请求,流程如下:



脏数据时间范围:更新数据库后,失效缓存前。这个时间范围很小,通常不会超过几毫秒。



先操作缓存

案例如下,有两个并发的请求,一个写请求,一个读请求,流程如下:



脏数据时间范围:更新数据库后,下一次对该数据的更新前。这个时间范围不确定性很大,情况如下:


1)如果下一次对该数据的更新马上就到来,那么会失效缓存,脏数据的时间就很短。


2)如果下一次对该数据的更新要很久才到来,那这期间缓存保存的一直是脏数据,时间范围很长。


结论:通过上述案例可以看出,先操作数据库和先操作缓存都会存在脏数据的情况。但是相比之下,先操作数据库,再操作缓存是更优的方式,即使在并发极端情况下,也只会出现很小量的脏数据。



失效缓存 or 更新缓存


更新缓存

案例如下,有两个并发的写请求,流程如下:



分析:数据库中的数据是请求B的,缓存中的数据是请求A的,数据库和缓存存在数据不一致。



失效缓存

案例如下,有两个并发的写请求,流程如下:



分析:由于是删除缓存,所以不存在数据不一致的情况。


结论:通过上述案例,可以很明显的看出,失效缓存是更优的方式。



如何保证数据库和缓存的数据一致性


在上文的案例中,无论是先操作数据库,还是先操作缓存,都会存在脏数据的情况,有办法避免吗?


答案是有的,由于数据库和缓存是两个不同的数据源,要保证其数据一致性,其实就是典型的分布式事务场景,可以引入分布式事务来解决,常见的有:2PC、TCC、MQ事务消息等。


但是引入分布式事务必然会带来性能上的影响,这与我们当初引入缓存来提升性能的目的是相违背的。


所以在实际使用中,通常不会去保证缓存和数据库的强一致性,而是做出一定的牺牲,保证两者数据的最终一致性。


如果是实在无法接受脏数据的场景,则比较合理的方式是放弃使用缓存,直接走数据库。


保证数据库和缓存数据最终一致性的常用方案如下:


1)更新数据库,数据库产生 binlog。


2)监听和消费 binlog,执行失效缓存操作。


3)如果步骤2失效缓存失败,则引入重试机制,将失败的数据通过MQ方式进行重试,同时考虑是否需要引入幂等机制。



兜底:当出现未知的问题时,及时告警通知,人为介入处理。


人为介入是终极大法,那些外表看着光鲜艳丽的应用,其背后大多有一群苦逼的程序员,在不断的修复各种脏数据和bug。




上文我们聊到了缓存设计模式中的 Cache Aside,并对常见的问题进行了延伸。


接着,我们来聊下缓存设计模式的其他几种:Read Through、Write Through、Write Behind Caching。



Read/Write Through


在 Cache Aside 中,应用层需要和两个数据源打交道:缓存、数据库,这增加了 应用层的复杂度,能否只和一个数据源打交道?


Read/Write Through 就是用来解决这个问题的,该模式下应用层只和缓存打交道,由缓存去操作和维护数据库。


该模式会让应用层变得更加简单,同时代码也会更简洁。



Read Through 


应用层查询数据时,当缓存未命中时,由缓存去查询数据库,并且将结果写入缓存中,最后返回结果给应用层。




Write Through


应用层更新数据时,由缓存去更新数据库。同时,当缓存命中时,写缓存和写数据库需要同步控制,保证同时成功。 




Write Behind Caching


Write Behind 又称为 Write Back,从应用层的视角来看和 Write Through 类似,在该模式下,应用层也是只需要和缓存一个数据源打交道,不同点在于:


Write Through 会立刻把数据同步写入数据库中,这样做的优点是操作简单,缺点是数据修改需要同时写入数据库,数据写入速度会比较慢。


Write Behind 会在一段时间之后异步的把数据批量写入数据库,这样的做的优点是:1)应用层操作只写缓存,应用层会觉得操作飞快无比;2)缓存在异步的写入数据库时,会将多个 I/O 操作合并成一个,减少 I/O 次数。


缺点是:1)复杂度高;2)更新后的数据还未写入数据库时,如果此时出现系统断电的情况,数据将无法找回。


Write Behind 的核心流程图如下:



Write Back 缓存模式由于其复杂性比较高,所以在业务应用中使用的比较少,但是由于其带来的性能提升,还是有不少优秀的软件采用了该设计模式,例如:linux 中的页缓存、MySQL 中的 InnoDB 存储引擎。


linux 中的 page cache(页缓存)采用的就是 write back 机制:用户 write 时只是将数据写到 page cache,并标记为 dirty,并没有真正写到硬盘上 。内核在某个时刻会将 page cache 里的 dirty 数据 wirteback 到硬盘上。


wikipedia 上有一张 Write Back 的流程图,如下,图中的 lower memory 可以简单的理解为数据库(硬盘):




最后


最近我将自己的原创文章整理分类了一下,可以在我公众号底部的“原创汇总”查看。


当你的才华还撑不起你的野心的时候,你就应该静下心来学习,愿你在我这里能有所收获。


原创不易,如果你觉得本文写的还不错,对你有帮助,请通过【点赞】让我知道,支持我写出更好的文章。



推荐阅读


两年Java开发工作经验面试总结

4 年 Java 经验面试总结、心得体会

5 年 Java 经验字节、美团、快手核心部门面试总结(真题解析)

面试必问的 Spring,你懂了吗?

面试阿里,HashMap 这一篇就够了

面试必问的线程池,你懂了吗?

面试必问的 MySQL,你懂了吗?

如何写一份让 HR 眼前一亮的简历(附模板)

如何准备好一场大厂面试

跳槽,如何选择一家公司

浏览 9
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报