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

前言

 

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

 

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

 

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

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

失效缓存 or 更新缓存?

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

 

 

正文

 

缓存查询通用流程

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

接下来我们一一分析。

 

 

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

 

先操作数据库

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

 

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

 

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

 

 

先操作缓存

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

 

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

 

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

 

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

 

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

 

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

 

 

失效缓存 or 更新缓存

 

更新缓存

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

 

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

 

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

 

 

失效缓存

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

 

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

 

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

 

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

 

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

 

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

 

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

 

 

Read/Write Through

 

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

 

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

 

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

 

 

Read Through 

 

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

 

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

 

 

Write Through

 

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

 

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

 

 

Write Behind Caching

 

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

 

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

 

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

 

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

 

Write Behind 的核心流程图如下:

 

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

 

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

 

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

 

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

 

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

 

 

最后

 

最近我将自己的原创文章整理分类了一下,汇总到了该文章下:原创汇总,后续的原创文章也会往该目录补充,喜欢我文章的同学可以收藏方便后续查阅。

 

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

 

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

 

推荐阅读

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

4 年 Java 经验,阿里网易拼多多面试总结、心得体会

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

921天,咸鱼到阿里的修仙之路

复习2个月拿下美团offer,我都做了些啥

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

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

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

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

跳槽,如何选择一家公司

如何准备好一场大厂面试

MySQL 8.0 MVCC 核心原理解析(核心源码)

你可能感兴趣的:(面试,我要进大厂,Redis,java,面试,经验分享,redis,程序人生)