当前位置: 首页 > news >正文

Redis面试

Redis缓存

缓存一致性

选择Cache Aside Pattern模式,在更新数据库的同时更新缓存

设置过期时间TTL,相当于给缓存一致性加了一个兜底的方案,在正常更新缓存失败的情况下,可以利用过期清理的方案保证于数据库的一致性

数据库增删改--->redis只做删:

我们对数据库进行增删改的操作时,redis没必要同步的去做增删改,只需要做删除就行,可以减少对redis不必要的占用,也不影响整个效果。除非该数据是热点数据,查询频率高,才需要同步增删改,大多数的数据不需要。

删除redis和数据库增删改的顺序问题:

1.先操作缓存,再操作数据库--->并发安全问题:

例如线程1修改数据线程2查询数据。修改数据的线程1先来,删除redis数据,再去操作数据库,但当线程1还没来得及修改数据库时,来了查询数据的线程2,查到redis没数据(已被线程1删除),就去查询数据库查到旧的数据,将旧数据缓存到redis,于是线程1删除的数据白删了。然后线程1才修改完数据库的数据,会发现数据库和缓存的数据不一致。

2.先操作数据库,再删redis

也有可能导致并发安全问题,但是概率极低

缓存穿透

指客户端请求的数据在数据库中不存在,缓存无法生效,从而导致请求穿透缓存,直接打到数据库的问题(就是说,明知道数据库没有该数据,故该缓存也不可能有数据,则会直接打到数据库)

假如有大量的线程一直请求不存在的数据,就会直接打到数据库,给数据库带来巨大的压力,甚至数据库崩溃。

解决方案

1.缓存空对象

当请求一个不存在的数据,数据库中也没有,则以请求的这个参数为key(这里指的是将要查询的数据作为key而不是来查的这个“人”作为key),在redis中缓存一个空的值(可能是null或者标记),下次再来查询时,则会在redis中查到这个特殊的值,就不会让其请求数据库相当于给不存在的数据也建立了一个请求,避免大量不存在的请求打到数据库。

优点:简单方便

缺点:占用内存-->解决方法-->设置过期时间

2.布隆过滤

存在于redis和客户端请求之间

优点:内存占用小,没有多余的key 

缺点:实现复杂。存在误差,不存在则一定不存在但是不能说一定存在,误差概率很小

补充:缓存预热

在项目刚开始运行时,去大量的查询数据库,提前批量的写入缓存中,当后续用户访问时可以直接命中缓存而不用临时建立缓存。

缓存雪崩

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

情况一大量的缓存key同时失效

解决方案:

在缓存预热的阶段,在不同的key的过期时间的基础上再加随机过期时间同时失效的概率降低

情况二Redis服务宕机

单点的Redis无法去避免,最佳方案是为其搭建集群,比如主从集群哨兵。哨兵可以监控主从集群的状态,当发现出现宕机时,可以重新选主,从而保证整个集群的高可用。即使这样也不能完全保证服务器不宕机,因此需要考虑宕机之后的解决方案

例如,当服务器宕机后,限制请求并发的量,减小数据库的压力。

其次,还可以进行降级策略,直接拒绝一部分请求,甚至可以对整个业务进行熔断阻止访问,缓存恢复之后再解除熔断。

给业务添加多级缓存,例如在浏览器上添加静态资源的缓存。

缓存击穿

也叫热点key问题,就是一个被高并发访问并且缓存重建业务比较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大冲击。

解决方案:

1.互斥锁

假设有一个线程查询时未命中,会想去进行缓存的重建(即查询数据库,查到的数据放入缓存的过程),但是为了避免无数的线程都去重建,要求对其加锁,只有获取锁成功的线程才能重建缓存数据,再释放锁。如果该线程没有释放锁,与此同时的其他线程无法获取锁也就无法重建缓存数据,需要休眠一会再重试。

特点:简单粗暴,但是如果重建的过程比较久,其他线程就需要等待,性能降低

2.监控数据

监控数据,实时调整:监控哪些数据是热门数据,实时的调整key的过期时长(将过期时长延长)。

Redis主从

主从同步原理

问题1:master是如何知道slave是第一次同步连接还是断开重连的

建立主从集群之前每个人都是master都有自己的replid。建立主从集群以后,主从的replid都变了变成了相同的replid。在第一次建立主从关系时,master会生成一个全新的replid,并且将该replid分享给每个slave。因此可以通过replid来进行判断是否是第一次来连接。

slave(此时还不是slave)尝试去成为slave,发出请求的同时还需要带上原本的replid,然后master根据其replid进行判断

replid不一致-->第一次来-->发送全部的数据

replid一致-->断开重连-->发送缺少的数据

问题2:如何发送全部数据

master执行bgsave(后台执行的命令,用于开启独立进程,把redis在内存中所有数据都持久化到硬盘中写到一个RDB文件里),生成RDB文件。也就是说RDB文件有master所有数据

此时,master只需要将该RDB文件发送给slave,slave将自己的数据清除把RDB文件加载到内存即可达到主从完全一致。

问题3:master如何知道slave缺失的是什么数据

offset相当于写入命令的量

将slave和master各自的offset做比较,看看缺失的哪部分命令,将命令发给slave即可实现增量同步

哨兵原理

思考:哨兵如何知道节点是否健康?哨兵应该选哪个为主?选主的角色转换是如何实现的?

1.哨兵如何知道节点是否健康?

心跳机制

2.哨兵应该选哪个为主?

3.选主的角色转换是如何实现的?

Redis内存回收

过期key处理

思考:Redis如何知道一个key是否过期?是不是TTL到期就立即删除?

1.Redis如何知道一个key是否过期?

dict和expires两个指针分别指向两个hash表。dict指向的表存放所有的键值对,expires指向的表里面的键是设置了过期时间的key值是key的到期时间

因此只需要拿expires指向表的key去查,就能拿到对应的到期时间,再将到期时间和当前时间进行对比就知道是否过期。

总结:在Redis中有两个hash表,一个记录的是原始的key-value,另外一个记录的是带有过期时间的key和它的到期时间,只需要拿它的key去查就知道是否过期。

2.是不是TTL到期就立即删除?

总结

内存淘汰策略

Redis数据持久化

redis之所以能够高速读写是因为它基于内存,但是也带来一个问题,在服务器宕机或者重启的情况下,内存里面的数据就会丢失。为了解决该问题,redis提供了持久化的技术来保证数据的持久性和可靠性

持久化方式:

1.RDB

  • 原理:RDB持久化是指在指定的时间间隔内内存中的数据集快照写入磁盘。实际操作过程是创建一个子进程(不是自己去做这件事,而是让子进程去做),先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

  • 节省空间:RDB文件是二进制的,通常比AOF文件更紧凑,因此可以节省磁盘空间。
  • 高性能:生成RDB快照时,Redis不会执行写入磁盘的操作,因此不会对性能产生很大影响。
  • 恢复速度快:RDB能够将数据集压缩存储在磁盘上,因此在恢复大数据集时比AOF更为迅速。
  • 数据备份简单:RDB文件非常适合用于数据备份,可以定时将RDB文件拷贝到其它存储介质上。

2.AOF

redis将自己执行的所有写入操作命令都记录下来,当redis重启之后,对所有写入命令进行回放以达到恢复数据

String和Hash的选择

​​​​​​​

1.String 和 Hash 哪一个存储对象数据更好?

(Hash是redis的一种数据结构/数据类型)

String 存储的是序列化后的对象数据,存放的是整个对象。Hash 是对对象的每个字段单独存储,可以获取部分字段的信息,也可以修改或者添加部分字段,节省网络流量。如果对象中某些字段需要经常变动或者经常需要单独查询对象中的个别字段信息Hash 就非常适合

String 存储相对来说更加节省内存,缓存相同数量的对象数据,String 消耗的内存约是 Hash 的一半。并且,存储具有多层嵌套的对象时也方便很多。如果系统对性能和资源消耗非常敏感的话,String 就非常适合。

在绝大部分情况,建议使用 String 来存储对象数据即可!

2.购物车信息用 String 还是 Hash 存储更好呢?

由于购物车中的商品频繁修改和变动,购物车信息建议使用 Hash 存储:

  • 用户 id 为 key
  • 商品 id 为 field,商品数量为 value

那用户购物车信息的维护具体应该怎么操作呢?

  • 用户添加商品就是往 Hash 里面增加新的 field 与 value;
  • 查询购物车信息就是遍历对应的 Hash;
  • 更改商品数量直接修改对应的 value 值(直接 set 或者做运算皆可);
  • 删除商品就是删除 Hash 中对应的 field;
  • 清空购物车直接删除对应的 key 即可。

 


http://www.mrgr.cn/news/31604.html

相关文章:

  • MySQL第八章,JDBC:java操作数据库
  • Springboot 微信小程序定位后将坐标转换为百度地图坐标,在百度地图做逆地址解析
  • Mybatis Plus 集成 PgSQL 指南
  • test 是 JavaScript 中正则表达式对象 (RegExp) 的一种方法,用于测试一个字符串是否匹配某个正则表达式
  • C++:线程(thread)的创建、调用及销毁
  • 外呼系统的功能都有哪些,怎么去选择?
  • AI政务产品:大模型驱动的数字人“边聊边办”
  • Unity3D URP 内置CSM分帧详解
  • 比较:wav2vec2_large_librivox.yaml与 wav2vec2_base_librispeech.yaml配置文件
  • Golang | Leetcode Golang题解之第420题强密码检验器
  • Linux 文件服务器-ftp匿名用户详解
  • 2024最新最全:超详细Nmap使用技巧(非常详细)零基础入门到精通,收藏这一篇就够了
  • Neo4j技术指南
  • nginx架构篇(三)
  • 火语言RPA流程组件介绍--获取关联元素
  • 搜索引擎onesearch3实现解释和升级到Elasticsearch v8系列(四)-搜索
  • 数字化转型的理论框架对比:从多维视角指导企业成功变革对比DPBOKIT4ITCOBITTOGAF
  • Oracle+11g+笔记(5)-Oracle数据库管理操作
  • 【大模型教程】基于 InternLM 和 LangChain 搭建知识库助手
  • 网络资源模板--Android Studio 图书借阅App
  • 设计模式-策略模式
  • 【大屏方案】可视化综合展示系统解决方案(Word原件2024)
  • [Web安全 网络安全]-XSS跨站脚本攻击
  • MySQL中的函数简单总结,以及TCL语句的简单讲解
  • 手机切换IP简单方法:掌握技巧,轻松实现IP变换‌
  • 浅谈计算机视觉的学习路径1