缓存击穿,缓存穿透和缓存雪崩

2024-05-13 10:08
文章标签 缓存 击穿 雪崩 穿透

本文主要是介绍缓存击穿,缓存穿透和缓存雪崩,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

什么是缓存?

缓存,就是数据交换的缓冲区,针对服务对象的不同(本质就是不同的硬件)都可以构建缓存。
目的是,把读写速度慢的介质的数据保存在读写速度快的介质中,从而提高读写速度,减少时间消耗。 例如:
  • CPU 高速缓存 :高速缓存的读写速度远高于内存。
    • CPU 读数据时,如果在高速缓存中找到所需数据,就不需要读内存
    • CPU 写数据时,先写到高速缓存,再回写到内存。
  • 磁盘缓存:磁盘缓存其实就把常用的磁盘数据保存在内存中,内存读写速度也是远高于磁盘的。
    • 读数据时,从内存读取。
    • 写数据时,可先写到内存,定时或定量回写到磁盘,或者是同步回写。

为什么要用缓存?

使用缓存的目的,就是提升读写性能。而实际业务场景下,更多的是为了提升读性能,带来更好的性能,更高的并发量。
日常业务中,我们使用比较多的数据库是 MySQL,缓存是 Redis 。Redis 比 MySQL 的读写性能好很多。那么,我们将 MySQL 的热点数据,缓存到 Redis 中,提升读取性能,也减小 MySQL 的读取压力。例如说:
  • 论坛帖子的访问频率比较高,且要实时更新阅读量,使用 Redis 记录帖子的阅读量,可以提升性能和并发。
  • 商品信息,数据更新的频率不高,但是读取的频率很高,特别是热门商品。

分布式缓存系统面临的问题

缓存与数据库双写不一致

一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求 “缓存+数据库” 必须保持一致性的话,最好不要做这个方案,即:读请求和写请求串行化,串到一个内存队列里去。
串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情况下多几倍的机器去支撑线上的一个请求。
最经典的就是缓存+数据库读写的模式(Cache Aside Pattern)。
  • 读的时候,先读缓存,缓存没有的话,再读数据库,然后取出数据后放入缓存,同时返回响应。
  • 更新的时候,先更新数据库,然后再删除缓存。

缓存穿透和缓存雪崩缓存穿透

概念:
指查询一个一定不存在的数据,由于缓存是不命中时被动写,即从 DB 查询到数据,则更新到缓存中,并且出于容错考虑,如果从 DB 查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要去 DB 查询,失去了缓存的意义。在流量大时,DB 可能就挂掉了。
举个栗子。系统A,每秒 5000 个请求,结果其中 4000 个请求是黑客发出的恶意攻击。数据库 id 是从 1 开始的,而黑客发过来的请求 id 全部都是负数。这样的话,缓存中不会有,请求每次都“视缓存于无物”,直接查询数据库。这种恶意攻击场景的缓存穿透就会直接把数据库给打死。
解决方案:
方案一: 缓存空对象,当从 DB 查询数据为空,我们仍然将这个空结果进行缓存,具体的值需要使用特殊的标识, 能和真正缓存的数据区分开,另外将其过期时间设为较短时间。
方案二: 使用布隆过滤器,在缓存的基础上,构建布隆过滤器数据结构,在布隆过滤器中存储对应的 key,如果存在,则说明 key 对应的值为空。这样整个业务逻辑如下:
  • 根据 key 查询缓存,如果存在对应的值,直接返回;如果不存在则继续执行。
  • 根据 key 查询缓存在布隆过滤器的值,如果存在值,则说明该 key 不存在对应的值,直接返回空,如果不存在值,继续向下执行。
  • 查询 DB 对应的值,如果存在,则更新到缓存,并返回该值,如果不存在值,则更新缓存到布隆过滤器中,并返回空。

缓存雪崩

概念:
缓存由于某些原因无法提供服务,所有请求全部达到 DB 中,导致 DB 负荷大增,最终挂掉的情况。
比如,对于系统 A,假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求,但是缓存机器意外发生了全盘宕机。缓存挂了,此时 1 秒 5000 个请求全部落数据库,数据库必然扛不住,它会报一下警,然后就挂了。此时,如果没有采用什么特别的方案来处理这个故障,DBA 很着急,重启数据库,但是数据库立马又被新的流量给打死了。
解决方案:
  • 缓存高可用:使用 Redis Sentinel 等搭建缓存的高可用,避免缓存挂掉无法提供服务的情况,从而降低出现缓存雪崩的情况。
  • 使用本地缓存:如果使用本地缓存,即使分布式缓存挂了,也可以将 DB 查询的结果缓存到本地,避免后续请 求全部达到 DB 中。当然引入本地缓存也会有相应的问题,比如本地缓存实时性如何保证。对于这个问题,可以使用消息队列,在数据更新时,发布数据更新的消息,而进程中有相应的消费者消费该消息,从而更新本地缓存;简单点可以通过设置较短的过期时间,请求时从 DB 重新拉取。
  • 请求限流和服务降级:通过限制 DB 的每秒请求数,避免数据库挂掉。对于被限流的请求,采用服务降级处理,比如提供默认的值,或者空白值。

缓存击穿

概念:
某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
解决方案:
  • 使用互斥锁 (mutex key):感知到缓存失效,去查询 DB 时,使用分布式锁,使得只有一个线程去数据库加载数据,加锁失败的线程,等待即可。
    • 获取分布式锁,直到成功或超时。如果超时,则抛出异常,返回。如果成功,继续向下执行。
    • 再去缓存中。如果存在值,则直接返回;如果不存在,则继续往下执行。因为,获得到锁,可能已经被“那个”线程去查询过 DB ,并更新到缓存中了。
    • 查询 DB ,并更新到缓存中,返回值。
  • 手动过期:redis 上从不设置过期时间,功能上将过期时间存在 key 对应的 value 里,如果发现要过期,通过一个后台的异步线程进行缓存的构建,也就是“手动”过期。

缓存并发竞争

某个时刻,多个系统实例都去更新某个 key。可以基于 zookeeper 实现分布式锁。每个系统通过 zookeeper 获取分布式锁,确保同一时间,只能有一个系统实例在操作某个 key,别人都不允许读和写。
要写入缓存的数据都是从 mysql 里查出来的,都得写入 mysql 中,写入 mysql 中的时候必须保存一个时间戳,从 mysql 查出来的时候,时间戳也要查出来。
每次要写之前,先判断一下当前这个 value 的时间戳是否比缓存里的 value 的时间戳要新。如果是的话,那么可以写,否则,就不能用旧的数据覆盖新的数据。
更多免费技术资料可关注:annalin1203

这篇关于缓存击穿,缓存穿透和缓存雪崩的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/985404

相关文章

利用Frp实现内网穿透(docker实现)

文章目录 1、WSL子系统配置2、腾讯云服务器安装frps2.1、创建配置文件2.2 、创建frps容器 3、WSL2子系统Centos服务器安装frpc服务3.1、安装docker3.2、创建配置文件3.3 、创建frpc容器 4、WSL2子系统Centos服务器安装nginx服务 环境配置:一台公网服务器(腾讯云)、一台笔记本电脑、WSL子系统涉及知识:docker、Frp

服务器雪崩的应对策略之----SQL优化

SQL语句的优化是数据库性能优化的重要方面,特别是在处理大规模数据或高频访问时。作为一个C++程序员,理解SQL优化不仅有助于编写高效的数据库操作代码,还能增强对系统性能瓶颈的整体把握。以下是详细的SQL语句优化技巧和策略: SQL优化 1. 选择合适的数据类型2. 使用索引3. 优化查询4. 范式化和反范式化5. 查询重写6. 使用缓存7. 优化数据库设计8. 分析和监控9. 调整配置1、

设置Nginx缓存策略

详细信息 Nginx服务器的缓存策略设置方法有两种:add_header或者expires。 1. add_header 1)语法:add_header name value。 2)默认值:none。 3)使用范围:http、server、location。 配置示例如下: add_header cache-control "max-age=86400";#设置缓存时间为1天。add

免费内网穿透工具 ,快解析内网穿透解决方案

在IPv4公网IP严重不足的环境下,内网穿透技术越来越多的被人们所使用,使用内网穿透技术的好处有很多。 1:无需公网ip 物以稀为贵,由于可用的公网IP地址越来越少,价格也是水涨船高,一个固定公网IP一年的成本要上万,而使用内网穿透技术则不需要公网IP的支持。 2:提高安全性 使用内网穿透技术,无需在路由器映射端口,我们知道黑客通常会使用端口扫描来寻找攻击对象,不映射端口能大大提高服务器的安全

【MyBatis学习7】MyBatis中的一级缓存

缓存的作用是减轻数据库的压力,提高数据库的性能的。mybatis中提供了一级缓存和二级缓存,先来看一下两个缓存的示意图:    从图中可以看出: 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。二级缓存是mappe

java NIO 缓存区之内核空间、用户空间和虚拟地址

IO是基于缓存区来做的,所谓的输入和输出就是从缓存区中移入和移出数据。以IO输入为例,首先是用户空间进程向内核请求某个磁盘空间数据,然后内核将磁盘数据读取到内核空间的buffer中,然后用户空间的进程再将内核空间buffer中的数据读取到自身的buffer中,然后进程就可以访问使用这些数据。     内核空间是指操作系统内核运行的空间,是为了保证操作系统内核的能够安全稳定地运行而为内核专

黑马苍穹外卖6 清理redis缓存+Spring Cache+购物车的增删改查

缓存菜品 后端服务都去查询数据库,对数据库访问压力增大。 解决方式:使用redis来缓存菜品,用内存比磁盘性能更高。 key :dish_分类id String key= “dish_” + categoryId; @RestController("userDishController")@RequestMapping("/user/dish")@Slf4j@Api(tags = "

Linux - 利用/proc/sys/vm/drop_caches实现手工清理系统缓存

文章目录 现象`buff/cache` 的作用和含义分析 `buff/cache` 占用大量内存的原因是否需要清理缓存及其方法 命令清理缓存方法1. `sync` 命令2. `echo 3>/proc/sys/vm/drop_caches` 命令 注意事项小结 现象 使用free 命令,看到 buff/cache 占用很多 。 free 命令用于显示系统内存的使用情

腾讯视频客户端缓存提取mp4文件工具

在腾讯视频客户端看过视频之后,可以利用本软件,从缓存中提取看过的视频。提取的视频将会保存为MP4文件。 下载地址:http://download.csdn.net/detail/u012107143/9769624 代码如下: @echo offsetlocal ENABLEDELAYEDEXPANSION:INPUT_PATHset video_path=F:\16

LeetCode 算法:LRU 缓存 c++

原题链接🔗: 难度:中等⭐️⭐️ 题目 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key)如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。void put(int key, i