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

news2024/12/29 10:47:32

目录

  • 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策略

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1544476.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每日一练:LeeCode-21、合并两个有序链表【链表+递归+非递归】

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 [1,2,4], l2 [1,3,4] 输出:[1,1,2,3,4,4] 示例 2: 输入:l1 [], l2 [] 输出:[…

家政服务管理平台设计与实现|SpringBoot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW,文末可获取本项目的所有资料。 推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java,…

阐述区块链“链游”项目3D/2D模式系统开发

随着区块链技术的不断发展,区块链游戏作为其应用领域之一也逐渐受到关注。在区块链游戏中,构建3D/2D模式系统是至关重要的,它决定了游戏的视觉效果、用户体验和技术实现。本文将探讨区块链游戏开发中构建3D/2D模式系统的关键要素和实现方法。…

字符驱动程序-LCD驱动开发

一、驱动程序的框架 总共分为五步: 1、自己设定或者系统分配一个主设备号 2、创建一个file_operations结构体 这个结构体中有操作硬件的函数,比如drv_open、drv_read 3、写一个注册设备驱动函数 需要register_chrdev(major,name,结构体)&#xff0…

动态多态的注意事项

大家好: 衷心希望各位点赞。 您的问题请留在评论区,我会及时回答。 多态的基本概念 多态是C面向对象三大特性之一(多态、继承、封装) 多态分为两类: 静态多态:函数重载和运算符重载属于静态多态&#x…

【嵌入式——QT】多语言界面

【嵌入式——QT】多语言界面 多语言页面开发步骤tr()函数 多语言页面开发步骤 第一步 在你编写的代码中添加tr()函数,方便之后可以精准的定位到你所需要翻译的部分。 第二步 在.pro文件中添加以下代码,这样会让你生成相应的.ts文件,ts文件是…

关于RPC

初识RPC RPC VS REST HTTP Dubbo Dubbo 特性: 基于接口动态代理的远程方法调用 Dubbo对开发者屏蔽了底层的调用细节,在实际代码中调用远程服务就像调用一个本地接口类一样方便。这个功能和Fegin很类似,但是Dubbo用起来比Fegin还要简单很多&a…

xcode生成静态库.a

一、生成静态库 1.打开 Xcode 创建一个新的 Static Library 工程,取名applestudio 2.创建工程完毕后,简化目录结构 删除系统自动创建的同名类:applestudio.h和applestudio.m 把自己的代码复制进去,如例子:guiconnect.h…

RSTP环路避免实验(华为)

思科设备参考:RSTP环路避免实验(思科) 一,技术简介 RSTP (Rapid Spanning Tree Protocol) 是从STP发展而来 • RSTP标准版本为IEEE802.1w • RSTP具备STP的所有功能,可以兼容STP运行 • RSTP和STP有所不同 减少了…

警务数据仓库的实现

目录 一、SQL Server 2008 R2(一)SQL Server 的服务功能(二)SQL Server Management Studio(三)Microsoft Visual Studio 二、创建集成服务项目三、配置“旅馆_ETL”数据流任务四、配置“人员_ETL”数据流任…

数据结构·二叉树(1)

目录 1 树的概念及结构 1.1 树的结构 1.2 树的概念 1.3树的表示 2 二叉树的概念及结构 2.1二叉树的概念 2.2 特殊的二叉树 2.3 二叉树的存储结构 1 树的概念及结构 1.1 树的结构 前面所学到的顺序表链表等,都是线性的数据结构,今天介绍的树&am…

Android Native Crash奔溃

一.Native Crash 简介 从 Android 系统全局来说,Crash 通常分为 App/Framework Crash,Native Crash,以及 Kernel Crash。 对于 App 层或者 Framework 层的 Crash(即 Java 层面 Crash),那么往往是通过抛出未捕获异常而导致的 Cras…

FPGA电平标准

1.LVTTL:(3.3v) 2.LVCOMS:(1.8v) 3.LVDS(1.8v):LVDS_25(2.5v) 4:如果是ddr3与fpga相连接fpga的vcco推荐(1.5v)…

flask_restful的基本使用

优势: Flask-Restful 是一个专门用来写 restful api 的一个插件。 使用它可以快速的集成restful api 接口功能。 在系统的纯api 的后台中,这个插件可以帮助我们节省很多时间。 缺点: 如果在普通的网站中,这个插件就没有优势了&…

【SQL】1517. 查找拥有有效邮箱的用户(正则表达式regexp)

前述 sql-正则表达式SQL学习笔记 – REGEXP 题目描述 leetcode 题目:1517. 查找拥有有效邮箱的用户 Code select * from Users where mail regexp ^[a-zA-Z][a-zA-Z0-9_.-]*leetcode\\.com$图片引用自 MySQL正则表达式

后端常问面经之操作系统

请简要描述线程与进程的关系,区别及优缺点? 本质区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位 在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之…

flask_restful规范返回值

使用方法 导入 flask_restful.marshal_with 装饰器 定义一个字典变量来指定需要返回的标准化字段,以及该字段的数据类型 在请求方法中,返回自定义对象的时候, flask_restful 会自动的读 取对象模型上的所有属性。 组装成一个符合标准化参…

定时器 c++ 基于时间线

获取当前时间std::chrono::system_clock::now(); std::chrono::time_point_cast<std::chrono::milliseconds>(now) 是 std::chrono 标准库中的一个函数调用&#xff0c;用于将时间点 now 转换为毫秒级别精度的时间点。 friend class timermanger; 表示将类 timermanger …

数据结构入门学习③——栈和队列

前言&#xff1a; 本篇博客主要介绍有关栈和队列数据结构相关知识 思维导图介绍&#xff1a; 开始之前&#xff0c;先介绍一下这篇博客主要介绍的主体内容&#xff1a; 栈和队列&#xff1a; 回顾线性表&#xff1a; 在刚才的思维导图里我们也了解到了——栈和队列都属于线性…

【vscode打开多文件夹】

1)将文件夹添加到工作空间中 2)文件夹方式展开 3)最终效果 小技巧&#xff1a; 文件夹的位置不对的话&#xff0c;可以拖动进行调整。