Java面试篇:Redis使用场景问题(缓存穿透,缓存击穿,缓存雪崩,双写一致性,Redis持久化,数据过期策略,数据淘汰策略)

目录

  • 1.缓存穿透
    • 解决方案一:缓存空数据
    • 解决方案二:布隆过滤器
  • 2.缓存击穿
    • 解决方案一:互斥锁
    • 解决方案二:设置当前key逻辑过期
  • 3.缓存雪崩
    • 1.给不同的Key的TTL添加随机值
    • 2.利用Redis集群提高服务的可用性
    • 3.给缓存业务添加降级限流策略
    • 4.给业务添加多级缓存
  • 4.双写一致性
    • 1.问题:redis做为缓存,mysql的数据如何与redis进行同步呢?
    • 2.异步通知保证数据的最终一致性
  • 5.Redis持久化的方式
    • 1.RDB
      • RDB的执行原理
    • 2.AOF
    • 3.RDB与AOF对比
  • 6.Redis数据过期策略
    • 1.惰性删除
    • 2.定期删除
  • 7.数据淘汰策略
    • 使用建议:

1.缓存穿透

①缓存穿透是指在使用缓存系统时,频繁请求一个不存在于缓存中的数据,导致缓存系统无法起到预期的加速作用,而直接请求数据库或其他底层存储系统。

②缓存系统一般通过将数据存储在内存中,以提高读取速度。当一个请求到达时,缓存系统会先检查是否有缓存数据,如果有则直接返回,如果没有则从底层存储系统中读取数据,并将其缓存起来以备后续使用。

③然而,如果频繁请求一个不存在于缓存中的数据,每次请求都会直接访问底层存储系统,无法从缓存中获取数据,导致缓存系统无法发挥作用。这种情况下,即使缓存系统存在,仍然会对底层存储系统产生很大的负载,甚至可能导致底层存储系统崩溃。

④缓存穿透可能会发生的原因包括恶意攻击、大量的并发请求和数据更新等。为了解决缓存穿透的问题,可以采取一些修复措施,例如使用布隆过滤器来过滤掉不存在的数据请求、设置缓存的过期时间对缓存进行预热等。

在这里插入图片描述

查询一个不存在的数据,mysql查询不到数据也不会直接写入缓存,就会导致每次请求都查数据库

解决方案一:缓存空数据

缓存空数据,查询返回的数据为空,仍把这个空结果进行缓存。
优点:简单
缺点:消耗内存,可能会发生不一致的问题

在这里插入图片描述

解决方案二:布隆过滤器

布隆过滤器(Bloom Filter)是一种用于判断某个元素是否存在于集合中的数据结构,它的特点是高效地判断一个元素是否存在于集合中,并且占用的内存空间相对较小。

布隆过滤器的核心是一个位数组(bit array)和一组哈希函数。当一个元素被加入布隆过滤器时,通过哈希函数将其映射为多个位数组的索引位置,并将这些位置的值设置为1。当判断一个元素是否存在于布隆过滤器时,同样通过哈希函数将其映射为多个位数组的索引位置,并检查这些位置的值是否都为1。如果有任何一个位置的值为0,则可以确定该元素不存在于集合中;如果所有位置的值都为1,则表示该元素可能存在于集合中,但不能确定是否真的存在,可能会存在误判的情况。

布隆过滤器的优点是占用的内存空间相对较小,且判断的速度非常快。但它也有一些缺点,其中最主要的就是可能存在误判的情况。由于布隆过滤器使用了多个哈希函数,所以在判断元素是否存在时可能会产生哈希冲突,导致误判。另外,布隆过滤器无法删除已经加入的元素,因为删除一个元素可能会影响到其他元素的判断结果。

布隆过滤器在实际应用中常用于缓存、数据库查询等领域,可以有效地提高查询效率和减轻底层存储系统的负载
在这里插入图片描述
布隆过滤器作用:布隆过滤器可以用于检索一个元素是否在一个集合中。

bitmap(位图)︰相当于是一个以(bit)位为单位的数组,数组中每个单元只能存储二进制数0或1

在这里插入图片描述

误判率:数组越小误判率就越大,数组越大误判率就越小,但是同时带来了更多的内存消耗。
在这里插入图片描述

优点:内存占用较少,没有多余key
缺点:实现复杂,存在误判

2.缓存击穿

缓存击穿:给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮.

在这里插入图片描述

解决方案一:互斥锁

  • 强一致
  • 性能差

当缓存失效时,不立即去load db,先使用如Redis的setnx去设置一个互斥锁,当操作成功返回时再进行load db的操作并回设缓存,否则重试get缓存的方法

在这里插入图片描述

解决方案二:设置当前key逻辑过期

  • 高可用,性能优
  • 不能保证数据绝对一致

①在设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间
②当查询的时候,从redis取出数据后判断时间是否过期
③如果过期则开通另外一个线程进行数据同步,当前线程正常返回数据,这个数据不是最新

在这里插入图片描述

3.缓存雪崩

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

在这里插入图片描述

1.给不同的Key的TTL添加随机值

合理设置缓存失效时间:根据业务场景和数据特点设置合理的缓存失效时间,避免大量数据在同一时间失效。可以根据数据的访问频率和重要性来设置不同的失效时间

解决方案主要是可以将缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件

2.利用Redis集群提高服务的可用性

哨兵模式、集群模式

  1. 数据分片:Redis 集群可以将数据按照一定的规则分片存储在不同的节点上,以提高数据处理能力和并发访问能力。

  2. 主从复制:通过配置 Redis 主从复制,可以将主节点的数据同步到多个从节点,实现数据的备份和故障恢复。当主节点发生故障时,从节点可以自动切换为主节点,保证服务的可用性。

  3. Sentinel 哨兵机制:Redis Sentinel 是 Redis 自带的高可用性解决方案,可以监控主节点和从节点的状态,并在主节点故障时自动选举新的主节点。哨兵还可以监控和修复其他节点的故障。

  4. 高可用性架构:搭建 Redis 集群时可以采用一主多从的高可用性架构,通过增加主节点和从节点来提高服务的可用性和并发处理能力。

  5. 集群模式:Redis 3.0 版本引入了 Cluster 集群模式,可以实现数据分片和自动故障转移。利用 Redis Cluster 可以搭建分布式的 Redis 集群,提高数据存储和处理的能力。

  6. 客户端链接失败重试:在客户端访问 Redis 集群时,可以设置失败重试机制,当某个节点连接失败时,自动尝试连接其他可用节点,提高服务的可用性。

3.给缓存业务添加降级限流策略

ngxin或spring cloud gateway

降级可做为系统的保底策略,适用于穿透、击穿、雪崩
限流降级:在高并发情况下,适当地对请求进行限流或降级,保护后端服务器的稳定性。

4.给业务添加多级缓存

Guava或Caffeine
使用多级缓存:将缓存分为多个层级,比如本地缓存和分布式缓存。
本地缓存可以使用内存,而分布式缓存可以使用 Redis。
这样即使 Redis 出现问题,本地缓存仍然可以提供部分服务。

4.双写一致性

1.问题:redis做为缓存,mysql的数据如何与redis进行同步呢?

可以通过以下几种方式将MySQL的数据同步到Redis缓存中:

  1. 手动同步:当MySQL中的数据发生变化时,手动编写代码将这些变化同步到Redis中。这可以通过使用MySQL的触发器或在代码中监听数据变化事件来实现。在触发器或事件中,将相应的数据插入、更新或删除到Redis中。

  2. 定时同步:定时将MySQL中的数据同步到Redis中。可以使用定时任务工具(例如Cron)来实现。设置一个定时任务,定期查询MySQL中的数据,然后将查询结果同步到Redis中。

  3. 增量同步:记录MySQL中数据的变化,只将发生变化的数据同步到Redis中。可以通过使用MySQL的二进制日志(binlog)来捕获MySQL中所有的修改操作,然后解析binlog,将修改的数据同步到Redis中。

  4. 使用消息队列:将MySQL中的数据变化通过消息队列传递给Redis,然后Redis消费这些消息来同步数据。可以使用消息队列工具(如RabbitMQ或Kafka)来实现。当MySQL中的数据发生变化时,将变化的数据作为消息发布到消息队列中,然后Redis作为消费者从消息队列中获取消息并将数据同步到Redis中。

无论使用哪种方式进行同步,都需要注意数据的一致性和并发性。在同步过程中,需要考虑数据的读写锁、事务处理和冲突解决等问题,以确保数据在MySQL和Redis之间的一致性。

双写一致性:当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致

在这里插入图片描述

  • 读操作:缓存命中,直接返回;缓存未命中查询数据库,写入缓存,设定超时时间
  • 写操作:延迟双删
    在这里插入图片描述

共享锁:读锁readLock,加锁之后,其他线程可以共享读操作
排他锁:也叫独占锁writeLock,加锁之后,阻塞其他线程读写操作

在这里插入图片描述

2.异步通知保证数据的最终一致性

使用MQ中间中间件,更新数据之后,通知缓存删除

在这里插入图片描述
利用canal中间件,不需要修改业务代码,伪装为mysql的一个从节点,canal通过读取binlog数据更新缓存
在这里插入图片描述

二进制日志(BINLOG)记录了所有的DDL(数据定义语言)语句和DML (数据操纵语言)语句
但不包括数据查询(SELECT、SHOW)语句。

5.Redis持久化的方式

1.RDB

RDB全称Redis Database Backup file (Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据

Redis内部有触发RDB的机制,可以在redis.conf文件中找到.

RDB的执行原理

bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据
完成fork后读取内存数据并写入RDB文件。

fork采用的是copy-on-write技术:

  • 当主进程执行读操作时,访问共享内存;
  • 当主进程执行写操作时,则会拷贝一份数据,执行写操作。

在这里插入图片描述

2.AOF

AOF全称为Append Only File(追加文件)。
Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件

AOF默认是关闭的,需要修改redis.conf配置文件来开启AOF。

AOF的命令记录的频率也可以通过redis.conf文件来配:

在这里插入图片描述

因为是记录命令,AOF文件会比RDB文件大的多
而且AOF会记录对同一个key的多次写操作,但只有最后一次写操作才有意义。
通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,用最少的命令达到相同效果。

Redis也会在触发阈值时自动去重写AOF文件。阈值也可以在redis.conf中配置

在这里插入图片描述

3.RDB与AOF对比

RDB和AOF各有自己的优缺点,如果对数据安全性要求较高,在实际开发中往往会结合两者来使用。

在这里插入图片描述

6.Redis数据过期策略

Redis对数据设置数据的有效时间,数据过期以后,就需要将数据从内存中删除掉。可以按照不同的规则进行删除,这种删除规则就被称之为数据的删除策略(数据过期策略)。

1.惰性删除

设置该key过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key.

  • 优点∶对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查
  • 缺点∶对内存不友好,如果一个key已经过期,但是一直没有使用,那么该key就会一直存在内存中,内存永远不会释放

2.定期删除

每隔一段时间,我们就对一些key进行检查,删除里面过期的key(从一定数量的数据库中取出一定数量的随机key进行检查,并删除其中的过期key)。

定期清理有两种模式:

  • SLOW模式是定时任务,执行频率默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf的hz选项来调整这个次数
  • FAST模式执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms

优点:可以通过限制删除操作执行的时长和频率来减少删除操作对CPU的影响。另外定期删除,也能有效释放过期键占用的内存。
缺点:难以确定删除操作执行的时长和频率。

Redis的过期删除策略:惰性删除+定期删除两种策略进行配合使用

7.数据淘汰策略

当Redis中的内存不够用时,此时在向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除掉,这种数据的删除规则被称之为内存的淘汰策略

①LRU (Least Recently Used)最近最少使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。
②LFU (Least Frequently Used)最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高。

Redis支持8种不同策略来选择要删除的key:

  • noeviction:不淘汰任何key,但是内存满时不允许写入新数据,默认就是这种策略
  • volatile-ttl:对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰
  • allkeys-random: 对全体key,随机进行淘汰
  • volatile-random:对设置了TTL的key,随机进行淘汰
  • allkeys-Iru:对全体key,基于LRU算法进行淘汰
  • volatile-lru:对设置了TTL的key,基于LRU算法进行淘汰
  • allkeys-lfu: 对全体key,基于LFU算法进行淘汰
  • volatile-lfu:对设置了TTL的key,基于LFU算法进行淘汰

使用建议:

1.优先使用 alkeys-lru 策略。充分利用LRU算法的优势,把最近最常访问的数据留在缓存中。如果业务有明显的冷热数据区分,建议使用。
2.如果业务中数据访问频率差别不大,没有明显冷热数据区分,建议使用allkeys-random,随机选择淘汰。
3.如果业务中有置顶的需求,可以使用volatile-lru策略,同时置顶数据不设置过期时间,这些数据就一直不被删除,会淘汰其他设置过期时间的数据。
4.如果业务中有短时高频访问的数据,可以使用allkeys-lfu或 volatile-lfu策略

上一篇:考研数学|概率论复习攻略


下一篇:【go从入门到精通】for和for range的区别