首页 > 数据库 >Redis缓存面试三兄弟:缓存穿透、缓存雪崩、缓存击穿

Redis缓存面试三兄弟:缓存穿透、缓存雪崩、缓存击穿

时间:2024-07-21 13:26:23浏览次数:13  
标签:方案 缓存 1.3 Redis 布隆 雪崩 过滤器 2.2

文章目录

0. 前言

Redis 是后端开发中最常用的缓存中间件,几乎每个项目都会使用 Redis ,所以 Redis 经常会在面试题中出现

在与 Redis 相关的面试题中,缓存穿透、缓存雪崩、缓存击穿这三个问题经常一起出现,所以这三个问题也被大家称为 Redis 缓存面试三兄弟

本文将为大家分析缓存穿透、缓存雪崩、缓存击穿产生的原因以及常见的解决方案

1. 缓存穿透

1.1 什么是缓存穿透

缓存穿透是指在高并发的情况下,客户端请求的数据在缓存中不存在,在数据库中也不存在,导致每次请求都直接打到数据库上,增大数据库的压力,甚至导致数据库服务不可用

1.2 缓存穿透产生的原因

1.2.1 恶意攻击

假设现在后台服务器有一个根据文章 id 获取文章详情的接口

/api/article/getDetail?id=1

如果黑客知道了接口的编写规则,就可以伪造假请求来冲击你的接口(比如用 id 等于 -1、id 等于 0 或者一些很大的 id 值来伪造假请求)

1.2.2 业务逻辑错误

由于业务逻辑的漏洞或者数据同步问题,导致某些应该存在的数据实际上并不存在

1.3 缓存穿透的解决方案

1.3.1 方案一:参数校验(需要与其它方案结合使用)

在接口层面进行参数校验,过滤掉非法请求或不合理的请求

1.3.2 方案二:缓存空值

对于查询结果为空的 key ,在缓存中存储一个特殊的空值(例如:{ key: null }),并设置较短的过期时间,这样后续的相同请求就会直接命中缓存,不会访问数据库,从而减轻数据库的压力

1.3.2.1 优点

逻辑简单,容易实现

1.3.2.2 缺点
  1. 数据不一致性:如果数据库中的数据发生变化(例如,原本不存在的数据现在被创建了),缓存中的空值不会自动更新,可能会导致短暂的数据不一致
  2. 过期时间设置困难:确定合适的过期时间比较困难,太短可能导致缓存穿透问题仍然存在,太长则可能导致数据不一致的问题
  3. 占用内存:当有多个 key 查询结果为空时,会占用一定的内存

1.3.3 方案三:使用布隆过滤器

使用布隆过滤器之后,整体的业务流程如下

在这里插入图片描述

1.3.3.1 什么是布隆过滤器

布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,由一个只存储 0 和 1 的数组和若干个哈希函数组成

布隆过滤器可以用来快速判断一个元素是否可能存在于某一个集合中

1.3.3.2 布隆过滤器的原理

在了解布隆过滤器的原理之前,我们先来了解一下 bit map (位图)

bit map 可以看做是一个以 bit 为单位的数组,数组的每个单元只存储 0 或 1 ,初始值都是 0

在这里插入图片描述

布隆过滤器的实现依赖于 bit map


当我们往数据库中写入数据(将数据写入数据库之后之后也会写入 Redis 缓存),会在布隆过滤器里做标记

下一次判断数据是否在数据库时,只需要通过布隆过滤器进行判断,如果数据在布隆过滤器没有被标记,说明该数据在数据库中不存在


布隆过滤器通过 3 个操作完成标记:

  1. 第一步,使用 N 个哈希函数分别对数据做哈希计算,得到 N 个哈希值
  2. 第二步,将第一步得到的 N 个哈希值对 bit map 的长度取模,得到每个哈希值在 bit map 中的对应位置
  3. 第三步,将每个哈希值在 bit map 中的对应位置的值设置为 1

举个例子,假设有一个 bit map 长度为 16 ,哈希函数个数为 3 的布隆过滤器,现在要往数据库中插入一条 id 为 1 的数据

在这里插入图片描述

这条 id 为 1 的数据会被 3 个哈希函数分别计算出 3 个哈希值,然后将 3 个哈希值对 16 取模,假设取模的结果为 1、3、7,那么 bit map 的第 1、3、7 个位置的值就会被设置为 1

当要判断 id 为 1 的数据是否存在于数据库时,只需要通过布隆过滤器判断 bit map 的第 1、3、7 位置的值是否全为 1

只要 1、3、7 中有任何一个位置为 0,就可以认为 id 为 1 的数据在数据库中不存在


布隆过滤器是基于哈希函数实现判断的的,存在哈希冲突的可能性,也就是说,使用布隆过滤器可能会出现误判的情况

下面是一个误判的例子

在这里插入图片描述


总结:

  • 如果布隆过滤器判断数据不存在,那么数据库中一定没有这个数据
  • 如果布隆过滤器判断数据不存在,数据库中不一定存在这个数据,有误判的几率

那么如何降低误判的几率呢

根据布隆过滤器的原理,我们可以知道

  • bit map 的长度越小,误判几率越大
  • bit map 的长度越大,误判几率越小

如果 bit map 的长度过小,很容易出现误判的情况;如果 bit map 的长度很大,内存开销也会增大


当然,这种造轮子的工作已经有前辈为我们做好了,比如 Redission 、Guava 等框架都提供了布隆过滤器的具体实现

1.3.3.3 优点

内存占用少、没有多余的 key

1.3.3.4 缺点

实现复杂、可能存在误判的情况

1.3.4 方案四:给业务添加降级限流策略(保底方案)

当系统扛不住较高的并发时,可以给业务添加降级限流策略(常见于微服务架构)

2. 缓存击穿

2.1 什么是缓存击穿

在某个时间点,缓存中某个热点数据过期了,此时恰好有大量的请求访问该热点数据,由于该数据在缓存中已经过期了,这些并发请求会同时打到数据库上,瞬间压垮数据库,这种现象被称为缓存击穿

2.2 缓存击穿的解决方案

2.2.1 方案一:互斥锁

保证同一时间只有一个业务线程更新缓存,对于未能获取互斥锁的请求,需要等待锁释放后再重新读取缓存

在这里插入图片描述

2.2.1.1 优点

能够保持数据的强一致性

2.2.1.2 缺点

由于使用了互斥锁,性能会下降

2.2.2 方案二:逻辑过期

我们不给热点数据设置真实的过期时间,而是给热点数据设置一个逻辑过期时间

我们往 Redis 中存入热点数据时,可以额外添加一个字段(通常是一个时间戳,比如{"id": 1, "title": "Redis缓存面试三兄弟", "expire": 1721394558})来记录热点数据的过期时间

  • 如果当前时间在热点数据的过期时间之前,说明热点数据还没有过期
  • 如果当前时间在热点数据的过期时间之后,说明热点数据已经过期了

当热点数据过期时,先返回旧数据,再由后台异步更新缓存

在这里插入图片描述

2.2.2.1 优点

能扛住更高的并发量

2.2.2.2 缺点

会出现短暂的数据不一致的情况

2.2.3 方案三:给业务添加降级限流策略(保底方案)

当系统扛不住较高的并发时,可以给业务添加降级限流策略(常见于微服务架构)

2.3 选择哪种方案

  • 如果业务对数据一致性要求很高,比如涉及到金钱交易的业务,首选互斥锁方案
  • 如果业务对数据一致性要求不高,更注重用户的体验,比如互联网项目,可以选择逻辑过期方案

3. 缓存雪崩

3.1 什么是缓存雪崩

缓存雪崩是指在同一时段大量的 key 同时失效或者 Redis 服务宕机,导致大量请求直接打到数据库,给数据库服务器带来巨大压力

3.2 缓存雪崩的解决方案

3.2.1 方案一:给不同的 key 设置不同的过期时间

给不同的 key 设置不同的过期时间,避免大量数据同时过期

3.2.2 方案二:搭建 Redis 集群

可以搭建 Redis 集群(哨兵模式、集群模式、主从模式)来提高 Redis 缓存服务的可用性

3.2.3 方案三:给业务添加多级缓存

例如,可以使用 Caffeine 作为一级缓存, Redis 作为二级缓存

3.2.4 方案四:给业务添加降级限流策略(保底方案)

当系统扛不住较高的并发时,可以给业务添加降级限流策略(常见于微服务架构)

标签:方案,缓存,1.3,Redis,布隆,雪崩,过滤器,2.2
From: https://blog.csdn.net/m0_62128476/article/details/140580942

相关文章

  • 动态规划-1:穷举遍历->map缓存->取消递归
    importjava.util.HashMap;importjava.util.Map;publicclassDynamicProgrammingAlgorithm{publicstaticvoidmain(String[]args){//比如要求一个数组的最长递增子序列的长度//比如是[1,4,2,5,3],那么[1,2,5],或者[1,2,3]都是最长递增子序......
  • redis常用命令大全
    以下是一个以表格形式总结的Redis命令概览:命令类别命令描述通用PING查看服务是否运行通用DBSIZE查看当前数据库中key的数目通用SELECTindex切换到指定的数据库通用FLUSHDB清空当前数据库的所有key及其值通用FLUSHALL清空所有数据库的所有key及其值通用QUIT/EXIT退出当前......
  • 同时使用线程本地变量以及对象缓存的问题
    同时使用线程本地变量以及对象缓存的问题如有转载请著名出处:https://www.cnblogs.com/funnyzpc/p/18313879前面  前些时间看别人写的一段关于锁的(对象缓存+线程本地变量)的一段代码,这段代码大致描述了这么一个功能:外部传入一个key,需要根据这个key去全局变量里面找是否存在,如......
  • Redis详解
    Redis(RemoteDictionaryServer)是一种开源的内存数据结构存储系统,作为NoSQL数据库的一种,Redis因其高性能和丰富的数据类型被广泛应用于缓存、会话管理、实时分析等场景。一、Redis简介1.1什么是Redis?Redis是一个开源的、基于内存的数据结构存储系统。它支持多种数据结构,如......
  • Redis7(二)Redis持久化双雄
    持久化之RDBRDB的持久化方式是在指定时间间隔,执行数据集的时间点快照。也就是在指定的时间间隔将内存中的数据集快照写入磁盘,也就是Snapshot内存快照,它恢复时再将硬盘快照文件直接读回到内存里面。RDB保存的是dump.rdb文件。自动触发默认redis是有三种自动触发的规则,在配置......
  • 【Memcached核心功能篇】缓存生命周期
    目录缓存生命周期管理数据过期策略时间戳和生存时间(TTL)自动刷新和更新机制示例1:使用TTL设置数据过期时间示例2:实现缓存穿透的解决方案示例3:解决缓存击穿问题缓存生命周期管理   缓存生命周期管理是任何使用缓存技术的系统中至关重要的一个方面。涉及到数据的......
  • Redis主从配置
    转载请注明出处:Redis主从配置的特点数据同步:主库(Master)负责处理写请求,并将数据更改同步到从库(Slave)。从库主要用于读请求和数据备份。读写分离:通过配置从库为只读,可以有效分散读请求,提升系统性能。高可用性和容错:即使主库出现故障,从库也能继续提供读服务,并在主库恢复后重新同......
  • 实现分布式锁,Zookeeper 与 Redis 哪个更好一点?
    1.为什么使用分布式锁?分布式锁有什么用途?(1)使用分布式锁的目的使用分布式锁的目的很简单,就是为了保证在同一时间里面,只有一个JVM进程可以实现对于共享资源的操作。确保数据的一致性在分布式环境中,多个节点可能会同时访问和修改同一数据或资源。分布式锁可以确保在任......
  • Java中的多级缓存设计与实现
    Java中的多级缓存设计与实现大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在现代应用程序中,多级缓存设计是一种常见的性能优化技术。多级缓存通过在不同层次上缓存数据来减少对底层存储系统的访问次数,提高系统的整体性能。本文将展示如何在Java中设计......
  • redis缓存雪崩,击穿,穿透,到底是什么?
    Redis缓存雪崩、击穿、穿透是缓存机制中常见的问题,这些问题都可能对系统的性能和稳定性产生严重影响。缓存雪崩是指当缓存层承载大量请求并有效保护存储层时,如果缓存层由于某些原因无法提供服务(如缓存数据大面积失效),导致所有请求都直接到达存储层,进而造成存储层请求量急剧增加......