【后端面经】【缓存】33|缓存模式:缓存模式能不能解决缓存一致性问题?-03 Refresh Ahead + SingleFlight + 删除缓存 + 延迟双删

简介: 【5月更文挑战第11天】Refresh Ahead模式通过CDC异步刷新缓存,但面临缓存一致性问题,可借鉴Write Back策略解决。SingleFlight限制并发加载,减少数据库压力,适合热点数据。删除缓存模式在更新数据库后删除缓存,一致性问题源于读写线程冲突。延迟双删模式两次删除,理论上减少不一致,但可能降低缓存命中率。选用模式需权衡优劣,延迟双删在低并发下较优。装饰器模式可用于实现多种缓存模式,无侵入地增强现有缓存系统。

Refresh Ahead


2024-05-12-13-19-21-image.png

Refresh Ahead是指利用CDC(capture Data Change)接口来异步刷新缓存的模式,这种模式在实践中也很常见,比如利用Canal来监听数据库的binlog,然后Canal刷新Redis。这种模式也有缓存一致性的问题,也是出在缓存未命中的读请求和写请求上。
2024-05-12-13-21-47-image.png

实际上,这个缓存一致性问题是可以解决的,也就是参考 Write Back 里面的策略。

如果读请求在回写缓存的时候,使用了 SETNX 命令,那么就没有什么大的不一致问题了。唯一的不一致就是数据写入到了数据库,但是还没刷新到缓存的那段时间。

SingleFlight

SingleFlight主要是为了控制住加载数据的并发量
2024-05-12-13-22-38-image.png

先简单介绍SingleFlight的原理,再补充它的优缺点

Singleflight模式是指当缓存未命中的时候,访问同一个key的线程或者协程中只有一个会真的加载数据,其他都在原地等待。

这种模式最大的优点就是可以减轻访问数据库的并发量,比如如果同一时刻有100个线程要访问key1,那么最终也只有1个线程去数据库中加载数据。这个模式的缺点是如果并发量不高,那么基本没有效果。所以热点之类的数据就很适合使用这个模式。

删除缓存

这算是业务中比较常见的用法,也就是在更新数据的时候先更新数据库,然后将缓存删除。
2024-05-12-13-25-27-image.png

删除缓存本身没有规定必须是业务代码来删除缓存,所以实际上也可以结合 Write Through 模式,让缓存去更新数据库,然后缓存自己删除自己的数据。
2024-05-12-13-25-43-image.png

这个模式依旧没有解决数据一致性的问题,但是它的一致性问题不是源自两个线程同时更新数据,而是源自一个线程更新数据,一个线程缓存未命中回查数据库。
2024-05-12-13-25-58-image.png

你在回答的时候要注意答出这一点。

删除是最常用的更新缓存的模式,它是指在更新数据库之后,直接删除缓存。这种做法可以是业务代码来控制删除操作,也可以结合 Write Through 使用。而且删除缓存之后会使缓存命中率下降,也算是一个隐患。如果偶尔出现写频繁的场景,导致缓存一直被删除,那么就会使性能显著下降。缓存未命中回查数据库叠加写操作,数据库压力会很大。

删除缓存和别的模式一样,也有一致性问题。但是它的一致性问题是出在读线程缓存未命中和写线程冲突的情况下。

然后你补充一句总结。

为了避免这种缓存不一致的问题,又有了延迟双删模式。

延迟双删

有两次删除操作
2024-05-12-13-27-04-image.png

延迟双删类似于删除缓存的做法,它在第一次删除操作之后设定一个定时器,在一段时间之后再次执行删除。

第二次删除就是为了避开删除缓存中的读写导致数据不一致的场景。
2024-05-12-13-27-25-image.png

那么是不是就不会有数据不一致的问题了?从理论上来说是可能的。第一个不一致出现在上图写入 a = 3 到第二次删缓存之间,还有一种不一致的可能如下图。
2024-05-12-13-27-37-image.png

但是这种可能性只是存在理论中,因为两次删除的时间间隔很长,不至于出现图片里的这种情况。所以你补充说明一下就可以了。

在这种形态之下,只需要考虑在回写缓存和第二次删除之间,数据可能不一致的问题。

紧接着再次说明这种模式的缺点。

延迟双删因为存在两次删除,所以实际上缓存命中率下降的问题更加严重。

选用什么模式

我觉得到这一步你已经非常困惑了,万一面试官问你应该使用哪个模式要怎么回答呢?坦白说,任何一种缓存模式都有各自的缺陷,所以你实际上选哪个都有好处,也都有问题。面试的时候你就可以根据自己的偏好来选择,只要分析清楚优劣,并解释清楚数据一致性问题就可以了。

如果你确实需要一个标准答案,那么你就回答延迟双删。

这么多模式里面,我比较喜欢延迟双删,因为它的一致性问题不是很严重。虽然会降低缓存的命中率,但是我们的业务并发也没有特别高,写请求是很少的。命中率降低一点点是完全可以接受的。

亮点方案:用装饰器模式实现缓存模式

这个亮点你可以考虑是否要使用,我建议你在实践中落地之后再拿去面试。但是你不需要把所有的模式都实现一遍,实现一下你项目中用到的就可以。

前面的缓存模式中,除了 Refresh Ahead 和 Cache Aside,其他的模式都可以使用装饰器模式来实现。我举一个使用缓存模式中 Read Through 模式的例子。你可以参考我给出的伪代码。

type Cache interface {
   
  Get(key string) any
  Set(key string, val any)
}

type ReadThroughCache struct {
   
  c Cache
  fn func(key string) any
}

func (r *ReadThroughCache) Get(key string) any {
   
    val := r.c.Get(key)
    if val == nil {
   
      val = r.fn(key)
      r.c.Set(key, val)
    }
    return val
}

你抓住关键词装饰器模式来描述这个解决方案。

我在我们公司利用装饰器模式,无侵入式地实现了其中的大部分模式。以 Read Through 为例,装饰器模式只需要在已有的缓存实现的基础上,为 Get 方法添加一个缓存中没有找到就去加载数据的额外逻辑就可以。

而且,如果你平时在公司的项目经历比较平淡,那么你完全可以在公司内部定义一个统一的 Cache 接口,提供基于 Redis 和本地内存的实现,同时提供这些缓存模式的实现,那么也算是一个比较有特色的项目了。

你就可以这样介绍你的项目。我在公司里面因为经常用到缓存,也经常使用缓存模式,所以我抽象了一个缓存接口,提供了基于 Redis 和本地内存的实现。在这个基础上,我还用装饰器模式实现了大部分缓存模式。对于开发者来说,他们只要会初始化装饰器就可以应用这个缓存模式。

后续你就可以和面试官讨论每一个缓存模式的细节。Beego 的缓存模块中有类似的实现,你可以参考。

目录
相关文章
|
3月前
|
canal 缓存 NoSQL
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
根据对一致性的要求程度,提出多种解决方案:同步删除、同步删除+可靠消息、延时双删、异步监听+可靠消息、多重保障方案
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
|
4月前
|
消息中间件 缓存 监控
如何保证缓存和数据库的一致性?
保证缓存和数据库的一致性的做法
|
16天前
|
存储 缓存 监控
后端开发中的缓存机制:深度解析与最佳实践####
本文深入探讨了后端开发中不可或缺的一环——缓存机制,旨在为读者提供一份详尽的指南,涵盖缓存的基本原理、常见类型(如内存缓存、磁盘缓存、分布式缓存等)、主流技术选型(Redis、Memcached、Ehcache等),以及在实际项目中如何根据业务需求设计并实施高效的缓存策略。不同于常规摘要的概述性质,本摘要直接点明文章将围绕“深度解析”与“最佳实践”两大核心展开,既适合初学者构建基础认知框架,也为有经验的开发者提供优化建议与实战技巧。 ####
|
1月前
|
缓存 NoSQL 关系型数据库
mysql和缓存一致性问题
本文介绍了五种常见的MySQL与Redis数据同步方法:1. 双写一致性,2. 延迟双删策略,3. 订阅发布模式(使用消息队列),4. 基于事件的缓存更新,5. 缓存预热。每种方法的实现步骤、优缺点均有详细说明。
|
2月前
|
缓存 监控 算法
小米面试题:多级缓存一致性问题怎么解决
【10月更文挑战第23天】在现代分布式系统中,多级缓存架构因其能够显著提高系统性能和响应速度而被广泛应用。
59 3
|
2月前
|
消息中间件 缓存 中间件
缓存一致性问题,这么回答肯定没毛病!
缓存一致性问题,这么回答肯定没毛病!
|
2月前
|
存储 缓存 NoSQL
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
73 4
|
2月前
|
存储 缓存 NoSQL
深入理解后端缓存机制的重要性与实践
本文将探讨在后端开发中缓存机制的应用及其重要性。缓存,作为提高系统性能和用户体验的关键技术,对于后端开发来说至关重要。通过减少数据库访问次数和缩短响应时间,缓存可以显著提升应用程序的性能。本文将从缓存的基本概念入手,介绍常见的缓存策略和实现方式,并通过实例展示如何在后端开发中有效应用缓存技术。最后,我们将讨论缓存带来的一些挑战及其解决方案,帮助您在实际项目中更好地利用缓存机制。
|
3月前
|
机器学习/深度学习 缓存 NoSQL
深度学习在图像识别中的应用与挑战后端开发中的数据缓存策略
本文深入探讨了深度学习技术在图像识别领域的应用,包括卷积神经网络(CNN)的原理、常见模型如ResNet和VGG的介绍,以及这些模型在实际应用中的表现。同时,文章也讨论了数据增强、模型集成等改进性能的方法,并指出了当前面临的计算资源需求高、数据隐私等挑战。通过综合分析,本文旨在为深度学习在图像识别中的进一步研究和应用提供参考。 本文探讨了后端开发中数据缓存的重要性和实现方法,通过具体案例解析Redis在实际应用中的使用。首先介绍了缓存的基本概念及其在后端系统性能优化中的作用;接着详细讲解了Redis的常见数据类型和应用场景;最后通过一个实际项目展示了如何在Django框架中集成Redis,
|
3月前
|
消息中间件 缓存 NoSQL
奇怪的缓存一致性问题
本文记录了缓存一致性问题的排查过程和解决方案,同时带读者朋友们一起回顾下相关的八股文。

热门文章

最新文章