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

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

前言


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


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


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

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

·       失效缓存 or 更新缓存?

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

 

正文


缓存查询通用流程

image.png

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

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

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

 

image.png

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

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

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

接下来我们一一分析。

 

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

先操作数据库

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

 

image.png

 

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

 

 

先操作缓存

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

 

image.png

 

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

 

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

 

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

 

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

 

 

失效缓存 or 更新缓存

 

更新缓存

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

 image.png

 

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

 

 

失效缓存

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

 

image.png

 

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

 

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

 

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 image.png

 

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

 

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

 

image.png

 

 

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

 

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

 

 

Read/Write Through

 

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

 

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

 

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

 

 

Read Through

 

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

image.png 

 

 

Write Through

 

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

 

image.png

 

 

Write Behind Caching

 

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

 

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

 

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

 

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

 

Write Behind 的核心流程图如下:

 image.png

 

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

 

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

 

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

 

image.png

image.png

 

最后


 

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

 

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

 

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

 

推荐阅读


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

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

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

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

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

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

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

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

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

跳槽,如何选择一家公司

如何准备好一场大厂面试

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

相关文章
|
2月前
|
缓存 Java 数据库连接
Mybatis缓存相关面试题有多卷
使用 MyBatis 缓存机制需要注意以下几点: 对于频繁更新和变动的数据,不适合使用缓存。 对于数据的一致性要求比较高的场景,不适合使用缓存。 如果配置了二级缓存,需要确保缓存的数据不会影响到其他业务模块的数据。 在使用缓存时,需要注意缓存的命中率和缓存的过期策略,避免缓存过期导致查询性能下降。
45 0
|
3月前
|
缓存 Java 应用服务中间件
面试官:如何实现多级缓存?
面试官:如何实现多级缓存?
185 1
|
20天前
|
缓存 NoSQL Redis
Python缓存技术(Memcached、Redis)面试题解析
【4月更文挑战第18天】本文探讨了Python面试中关于Memcached和Redis的常见问题,包括两者的基础概念、特性对比、客户端使用、缓存策略及应用场景。同时,文章指出了易错点,如数据不一致和缓存淘汰策略,并提供了实战代码示例,帮助读者掌握这两款内存键值存储系统的使用和优化技巧。通过理解其核心特性和避免常见错误,可以提升在面试中的表现。
26 2
|
16天前
|
消息中间件 缓存 数据库
如何保证缓存与数据库的数据一致性?
如何保证缓存与数据库的数据一致性?
31 5
|
1月前
|
缓存 NoSQL 算法
17- 数据库有1000万数据 ,Redis只能缓存20w数据, 如何保证Redis中的数据都是热点数据 ?
保证Redis中的20w数据为热点数据,可以通过设置Redis的LFU(Least Frequently Used)淘汰策略。这样,当数据库有1000万数据而Redis仅能缓存20w时,LFU会自动移除使用频率最低的项,确保缓存中的数据是最常使用的。
62 8
|
11天前
|
消息中间件 缓存 关系型数据库
数据库和缓存如何保证一致性?
数据库和缓存如何保证一致性?
|
11天前
|
存储 缓存 算法
面试遇到算法题:实现LRU缓存
V哥的这个实现的关键在于维护一个双向链表,它可以帮助我们快速地访问、更新和删除最近最少使用的节点,同时使用哈希表来提供快速的查找能力。这样,我们就可以在 O(1) 的时间复杂度内完成所有的缓存操作。哈哈干净利索,回答完毕。
|
28天前
|
缓存 运维 NoSQL
面试分享:Redis在大数据环境下的缓存策略与实践
【4月更文挑战第10天】探索Redis在大数据缓存的关键作用,本文分享面试经验及必备知识点。聚焦Redis数据结构(String、List、Set、Hash、Sorted Set)及其适用场景,缓存策略(LRU、LFU、TTL)与过期机制,集群和数据分片,以及性能优化和运维技巧。通过代码示例深入理解,助你面试成功,构建高效缓存服务。
47 4
|
2月前
|
缓存 应用服务中间件 数据库
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(多级缓存设计分析)
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(多级缓存设计分析)
41 1
|
2月前
|
存储 缓存 Java
什么!?实战项目竟然撞到阿里面试的原题!???关于MyBatis Plus的缓存机制
什么!?实战项目竟然撞到阿里面试的原题!???关于MyBatis Plus的缓存机制